06-python中的集合类-01

目录

  • 06-python中的集合类-01
    • namedtuple 容器
      • 创建 namedtuple
      • namedtuple 的一些特点
      • 为啥要有namedtuple 呢?
    • defaultdict
      • defaultdict 创建
      • 来看一个leetcode题目
    • 总结
    • 参考文档

06-python中的集合类-01

​ 今天 , 我们来介绍 一下python 中 一些 集合类 ,python中 一些 内置的一些库 已经提供了很多 比较好的数据结构,方便 我们在今后的工程中进行使用, 之前 有把 python 基础的数据结构 , list, set ,tuple ,int ,float 这些基础的数据结构 介绍完了,想必 你们也有一定的了解. 今天 我来介绍一些 . 这些 数据结构 有时候 特别方便使用 .

比如 namedtuple ,deque 等 这些 数据结构 都在 collections这个内置包里面.

目前来说 这里 有那么多, 这里从官方文档 拷贝下来的.

namedtuple() factory function for creating tuple subclasses with named fields
deque list-like container with fast appends and pops on either end
ChainMap dict-like class for creating a single view of multiple mappings
Counter dict subclass for counting hashable objects
OrderedDict dict subclass that remembers the order entries were added
defaultdict dict subclass that calls a factory function to supply missing values
UserDict wrapper around dictionary objects for easier dict subclassing
UserList wrapper around list objects for easier list subclassing
UserString wrapper around string objects for easier string subclassing

namedtuple 容器

看到这个名字 感觉它 是什么呢? , 翻译一下 命名元祖, 我们知道元祖 是通过 下标 来访问元素的, 命名元祖 难道 是要给下标 起个名字吗? 答案是 是的.

来让我们 感受一下 这个 容器 如何使用?

>>> from collections import namedtuple

>>> # 定义一个命名元祖
>>> Point = namedtuple('point', ['x', 'y'])
>>> p = Point(10, 20)
>>> print(p)
... 
point(x=10, y=20)
>>> print(p.x, p.y)
10 20
>>> print(p[0], p[1])
10 20

从上面的 例子看出来, p 这个 新生成的对象. 有两个属性, x,y 可以通过 p.x , p.y 来访问 这两个属性, 同样 p 还有了元素的性质, 可以通过 下标 进行访问.

这里下标为0 的索引, 同时可以索引访问 p[0] 还可以使用 p.x 进行访问. 相当于 给 下标为0 的索引 '新起’了一个名字 叫 x. 我想 这就是namedtuple 的来历吧.

打开 consle , 查看一下,

06-python中的集合类-01_第1张图片

我发现确实 有两个属性, x,y , 这和我刚刚猜测的差不多,这样 p 对象里面 已经新生成了两个属性. x,y 分别 用来 表示小标 0,下标 1 的位置.

创建 namedtuple

首先 通过上面的例子演示 ,我们可以知道 namedtuple 可以通过 预先定义的属性,来访问元素.

如何定义一个 namedtuple , 首先 第一个位置 可以认为 是类名, 第二个位置 就是属性值, 这些属性值可以用 逗号 隔开 或者 通过list 来区分 就好.

大部分 情况下,通过这两种方式 生成 namedtuple , 之后 给这个nametuple 赋值 就可以了. 注意赋值的时候 位置要对应起来, 比如 0 --> name , 1 —> age , 这个 位置顺序是不能错的. 这样 计算机 才能明白,如何把 位置 和属性 建立起来 联系.

>>> 
>>> Person = namedtuple('Person','name,age,height,gender,hobby')
... 
... person = Person('frank',19,'165','male','swimming')
... 
>>> 
>>> person
Person(name='frank', age=19, height='165', gender='male', hobby='swimming')


>>> 
>>> Person = namedtuple('Person',['name', 'age', 'height', 'gender', 'hobby'])
>>> Person
<class '__main__.Person'>
>>> person = Person('frank',19,'165','male','swimming')
... 
>>> person
Person(name='frank', age=19, height='165', gender='male', hobby='swimming')

当然 关于创建 namedtuple 还有一些其他的 参数,这里我不展开了,可以参考 我给出的一个链接python中namedtuple的用法

namedtuple 的一些特点

说到 nameetuple 的特点,首先之前还记得 我们说过 python 的 中tuple 的特点吗? 让我来回忆一下,

01-Python中的数据类型-04-tuple,dict,set

最大的特点 就是只读,就是不可修改. 即一旦 元祖创建完成,就不可以 被修改了,所有的值 只能 作为 读的特点. 还有就是 tuple 本身 是有序的,通过索引 进行访问 .

好,那么namedtuple是不是 也是这样的 特点呢? 回答: 是的

来看下面的例子:

>>> Person = namedtuple('Person','name,age,height,gender,hobby')

>>> person = Person('frank',19,'165','male','swimming')
>>> person
Person(name='frank', age=19, height='165', gender='male', hobby='swimming')


>>> # 尝试修改tuple 
>>> person.name='test111'
Traceback (most recent call last):
  File "", line 1, in <module>
AttributeError: can't set attribute
>>> person[0]='aaaaa'
Traceback (most recent call last):
  File "", line 1, in <module>
TypeError: 'Person' object does not support item assignment

为啥要有namedtuple 呢?

​ 其实这个问题 很简单, 假设 你有一个元祖里面 有大量的元素 , 我并不知道 他们的位置信息,我就可以通过一个属性,找到对应的值.

从计算机的角度来说,只要给一个位置, 我就是那个位置对应的元素, 但是 实际上, 人脑 不能 长期记住那么多的位置, 会忘记. 这个时候 我就通过属性 轻易获取 对应的值.

使用这个方便 人 来记住某个值, 给某个值 起一个 名字 ,这样 我们 就可以 找到他们了.

有了namedtuple 你可能会想 , 为啥不直接使用字典呢?


person  = {
     
    'name':'frank',
    'age':19,
    'height':165,
    'gender':'male',
    'hobby':'swimming'
}

这样 我一样可以实现 直接 获取 对应 key 的值呀.

首先, 这种想法 没有错, 当然 可以通过定义字典,来通过key 来获取对应的值. 也很方便.

但是 字典本身 是可以改变的,就是说, name 的值 可能 有被修改的风险, 这个namedtuple 就可以保证 值不能修改.

第二点 字典 本身很灵活, 而我们设计nametuple 就是来保存一些常量,或者不会变化的值. 还有 nametuple 满足tuple 的所有特性,本质来说 是 tuple , 只是把tuple 做了一点小小的改进.

defaultdict

defaultdict 这个实际工作中 用的挺多的, 所以还是要介绍一下.

首先我们应该知道dict 的一些特点 , 无序,可变性, 主要是这两个特点.

如果记不住了,可以看看 之前的文章. 01-Python中的数据类型-04-tuple,dict,set

在字典中获取一个 key 有两种方法, 第一种 get , 第二种 通过 [] 获取.

如果通过第二种方式获取要求,字典本身要有这个key ,如果没有则会报错KeyError, 因为这个key 不存在.

>>> 
>>> person
{
     'name': 'frank', 'age': 19, 'height': 165, 'gender': 'male', 'hobby': 'swimming'}
>>> person.get('name')
'frank'
>>> person['aaa']
Traceback (most recent call last):
  File "", line 1, in <module>
KeyError: 'aaa'

有时候 为了 解决一类问题, 如果 我们想要 设置一个 key ,

如果这个key 存在 就设置一个值, 这个时候可以直接设置这个值.

如果不存在 就添加 这个key 并且 给一个 默认值.

举一个场景

​ 如果我有篮子 水果. 苹果 ,橘子,梨,葡萄,香蕉, 现在 让 你统计每个品种的个数.

现在 我们开始使用最原始的方式字典,

from collections import defaultdict

from typing import Dict, List

fruits = ['apple', 'orange', 'pear', 'bananna',
          'orange', 'pear', 'bananna', 'orange',
          'pear', 'pear', 'bananna', 'grape',
          'bananna', 'orange', 'pear', 'bananna',
          'pear', 'bananna', 'grape']


def count_fruits(fruits: List) -> Dict:
    counter = {
     }

    for fruit in fruits:

        if fruit in counter:
            counter[fruit] += 1
        else:
            counter[fruit] = 1

    return counter
  

if __name__ == '__main__':
    print(count_fruits(fruits))  # {'apple': 1, 'orange': 4, 'pear': 6, 'bananna': 6, 'grape': 2}
    pass

上面的方式 好像没有什么问题 . 但是每次 遍历 都要判断 fruit 有没有在字典中 出现过, 就是 for 循环里面 有一个 一个 if 语句. 这样写 功能 是没有问题, 但是并不好.

有没有更好的方式呢? 你还记得 setdefault 的方法吗 ?

def count_fruits2(fruits: List) -> Dict:
    counter = {
     }

    for fruit in fruits:
        counter.setdefault(fruit, 0)
        counter[fruit] += 1
    return counter

上面 看起来 比之前 好一些 的方法, 但是没有更好的方式呢 ? defaultdict 就是可以实现更加优雅的方式.

def count_fruits3(fruits: List) -> Dict:
    counter = defaultdict(int)

    for fruit in fruits:
        counter[fruit] += 1
    return dict(counter)
    

这里把 counter 定义成一个 defaultdict . counter[fruit] += 1 当执行 这句话的时候,如果存在key 那么直接 加一, 如果没有 这个key ,直接生成一个key, value 初始值为0 . 看看是不是很方便.

defaultdict 创建

首先 defaultdict 继承 dict 所有dict 的方法, defaultdict 都可以使用. 这就是继承的好处.直接复用了之前已经写好的代码.

class defaultdict(dict):pass

能够生成 dict 的方法, defaultdict 也可以直接生成.

在 defaultdict 中 有一个参数, defaul_factory 这个就是代表 你要给默认值的类型. 比如 int, list ,dict 等等.

def __init__(self, default_factory=None, **kwargs):pass

看下面的例子:

>>> 
>>> couter = defaultdict(int)
>>> couter['one'] =1
>>> couter['two']
0

>>> couter
defaultdict(<class 'int'>, {
     'one': 1, 'two': 0})

当我尝试访问 key=two 的时候,这个时候字典没有这个key, 它 会自动生成一个默认值,作为这个key 的value 值.

>>> from collections import defaultdict

>>> counter = defaultdict(list)
>>> counter
defaultdict(<class 'list'>, {
     })
>>> counter['number'].append(1)
>>> counter['number'].append(2)
>>> counter['number'].append(3)
>>> counter
defaultdict(<class 'list'>, {
     'number': [1, 2, 3]})

看看这样 我们就不用担心 设置一个默认值了, 如果发现 没有这个值,defaultdict 会根据 default_factory 来生成 对应的值的.

这就是 defaultdict 的作用了. 实际应用中 可以有很多方式, 知道这样的方法,可以使用代码更加的简洁.

来看一个leetcode题目

举例 leetcode 习题 leetcode217 题目 存在重复元素

给定一个整数数组,判断是否存在重复元素。

如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false

这个题目思路:

只要统计每个元素出现的次数 就可以了, 如果有一个元素出现两次 返回True, 所有的元素 都统计完了,还没有出现两次, 就返回 False .

from collections import defaultdict

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:

        counter = defaultdict(int)
        for num in nums:
            counter[num] += 1
            if counter[num] >= 2:
                return True
        return False

总结

​ 这篇文章 介绍 python 中的集合类 namedtuple, defaultdict . 集合类 有很多,后面在介绍, 有了集合类 可以更加方便的写出 更加优美的代码,灵活运用这些集合类,可以提高自己的工作效率. 后续 我会继续 写一下 其他的集合类,

加油,继续保持学习哦.

分享一句心灵鸡汤

一个人 在才华被认可前, 都要苦熬一段时间的 不是吗?
——《 短片小说选》欧.亨利

参考文档

collections 官方文档

python中namedtuple的用法

01-Python中的数据类型-04-tuple,dict,set

分享快乐,留住感动.2020-09-06 16:58:58 --frank

你可能感兴趣的:(和我一起学习Python3,基础,python,collection模块)