目录
Collection 模块中的各种方法和使用,基于 Python3 的版本学习。这个模块实现了基于 Python 中的一些基础模块,如 dict, list, tuple 和 set,的上层应用,在基础模块上增加了很多方法,在实际使用的过程中也显得简单方便,更有实践性。
参考:
collections
— 容器数据类型
函数 | 作用 |
---|---|
namedtuple | 创建命名元组子类 |
deque | 实现了双端队列 |
ChainMap | 将多个映射集合到一个dict里 |
Counter | 提供了可哈希对象的计数功能 |
OrderedDict | 有序字典,保存了他们被添加的顺序 |
defaultdict | 为字典查询提供一个默认值 |
tuple
是一个不可变元组(不可变对象),同时也是 iterable
对象,表示可迭代的对象,可以进行迭代。
name_list = ('lim', 'lim2') for name in name_list: print(name)
在对象中实现了 __iter__
和 __getitem__
这两个魔法函数,就可以被认为是可迭代的对象,就可以使用 for in
的方式去迭代对象。
如果尝试去修改的话就会报错:
TypeError: 'tuple' object does not support item assignment
还有一个用法是拆包。
name1, name2 = name_list print(name1, name2)
可以表明属性信息。(位置信息关系)
people_tuple = ('lim', 'jaelyn', 12, 'diqiu') first_name, name, age, addr = people_tuple print(first_name, name, age, addr) # lim jaelyn 12 diqiu name, *other = people_tuple print(name, other) # lim ['jaelyn', 12, 'diqiu']
也可以使用星号通配符了来一次性匹配多个信息。
这里需要注意,这种星号表达式只是在 Python3 才开始使用的,在 Python2 中是不支持的。
name_tuple = ('lim', [1, 2, 3]) print(name_tuple) # ('lim', [1, 2, 3]) name_tuple[1].append(22) print(name_tuple) # ('lim', [1, 2, 3, 22])
tuple
这个对象里面的内容一般,来说是不允许修改的,但这个只是相对来说的,不是绝对的,添加到 tuple
里的值的时候注重的是内容的 id
,而不是具体里面的内容,例如上面的例子,只是修改了里面list
中的内容,但是并没有改变这个list
的id
,所以tuple
允许这样的修改。
如果尝试直接修改,导致修改了里面元素的id的话,就会报错:
name_tuple[1] = [123] # TypeError: 'tuple' object does not support item assignment
所以,对于一些可变的的数据变量的话,就不推荐这么使用,会使得tuple
的不可变性失去意义。
name_tuple = (1, 'j') user_info_dict = {} user_info_dict[name_tuple] = 'lim' print(user_info_dict) # {(1, 'j'): 'lim'} name_tuple = [1, 'j'] user_info_dict[name_tuple] = 'lim' # unhashable type: 'list'
这样就可以看出,在使用 tuple
的时候,作为一个可哈希的对象,允许作为字典中的key来使用。
namedtuple
通过这个可以实现一个类。
from collections import namedtuple User = namedtuple("User", ['name', 'age', 'height']) user = User(name='lim', age=12, height=120) print(user) # User(name='lim', age=12, height=120) print(type(user)) # <class '__main__.User'> print(user.name, user.age, user.height) # lim 12 120
这样就可以看出,这个模块就可以简化我们创建一个类,并且在构造函数中赋值的操作,并且返回的类是一个真正可以使用的类。这一种写法适用于没有函数的类,这一种写法的好处:
- 简单,可以很快的就实现一个简单的类,并且按照正常类的用法来使用。
- 省空间,相比较于创建一个新的类,这一种写法可以省去类中的其他不必要的默认函数和变量。
用作数据处理的时候会很大用处。
同样,也可以在初始化这个类的时候,采用 tuple
作为参数,以这样的方式传递参数创建一个对象。
user_tuple = ('lim', 12, 13) user = User(*user_tuple)
这样的话,参数就可以按照顺序映射在参数上。
官方文档也有 tuple
的用法:
"""Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """
由源码也可以看出,在 namedtuple
创建的时候,会执行一个 exec()
语句,这是Python的内置函数,可以将字符串单做代码来行,从中可以看出,在执行的时候,会执行 _class_template
这个模板类,这个就是namedtuple
可以生成类的原因。在这个模板中,也可以看出,有些内置的函数也会执行。
例如其中的 _make()
方法,可以传递一个可迭代对象,实现对象的创建。
user_new = User._make(user_tuple) print(user_new)
其中,参数只要是可迭代的对象都可以。
user_info_dict = user_new._asdict() print(user_info_dict) # OrderedDict([('name', 'lim'), ('age', 12), ('height', 13)])
还有 _asdict()
方法,可以将 tuple
对象装换成 OrderedDict
。
并且,这个生成的类是继承 tuple
的,一样可以进行拆包。
defaultdict
这个模块是用 C 语言实现的一个类,所以性能上来说是很高的。
当我们需要在一个列表中使用统计列表中元素的出现次数的时候,我们就可以采用这中方式。这样可以避免做过多的统计逻辑。如果没有使用这个模块的话,正常的操作:
user_dict = {} users = ['lim', 'jaelyn', '1', 'jaelyn', '1'] for user in users: user_dict.setdefault(user, 0) user_dict[user] += 1 print(user_dict) # {'lim': 1, 'jaelyn': 2, '1': 2}
使用这个模块的时候,需要传递一个参数表明初始化类型。
default_dict = defaultdict(int) for user in users: default_dict[user] += 1 print(default_dict) # defaultdict(<class 'int'>, {'lim': 1, 'jaelyn': 2, '1': 2})
这个模块会在没有这个 key
的时候,根据一开始传递的类型而进行初始化操作。这样可以避免再我们没有创建这个 key
而去用的时候的报错。
deque
双端队列。
相对于普通的 list
,这个模块可以对这个队列的两端进行操作,不当当只是对队列的右端进行操作。其中的 pop
, append
, extend
等,都相对应的有左边的操作,例如 popleft()
、 appendleft()
等。
注意其中的 extend
这个函数只是对当前元素进行修改,而不会返回一个新的对象。还有 reverse
(反转队列)也是这样。
应用场景:我们使用的 from queue import Queue
中的队列就是双端队列。
最重要的一点,deque
是线程安全的,而 list
不是线程安全的。deque
是通过 GIL 全局锁进行保护的。
Counter
进行数据统计使用。
from collections import Counter users = ['lim', 'jaelyn', '1', 'jaelyn', '1'] user_counter = Counter(users) print(user_counter) # Counter({'jaelyn': 2, '1': 2, 'lim': 1})
这个模块可以根据我们传递的参数队列,统计里面的数量 ,并且按照统计结果,按照数量大小顺序排列。
也可以使用 update
方法,更行统计的信息。
user_counter.update(["lim"])
而 Counter
对象来说,本身就是一个可迭代的对象,还可以这样使用,将 Counter
对象当做参数传递到 update
方法中使用。
user_counter = Counter(users) user_counter02 = Counter(["jaelyn"]) user_counter.update(["lim"]) user_counter.update(user_counter02) print(user_counter)
还有一个常用的函数: most_common
可以统计前 n 个出现次数最多的元素。
print(user_counter.most_common(200)) # [('jaelyn', 3), ('lim', 2), ('1', 2)]
使用的是堆的数据结构,来实现最大统计问题。
OrderedDict
作为 dict 的子类,有其中的所有方法。
from collections import OrderedDict user_dict = OrderedDict() user_dict['b'] = 'lim1' user_dict['a'] = 'lim2' user_dict['c'] = 'lim3' print(user_dict) # OrderedDict([('b', 'lim1'), ('a', 'lim2'), ('c', 'lim3')])
可以按照添加进去的顺序进行。这里需要注意,在 Python3 中,dict 也是有序的,也就会会按照添加元素的顺序进行排序,但是在 Python2 中,是无序的。
print(user_dict) # OrderedDict([('b', 'lim1'), ('a', 'lim2'), ('c', 'lim3')]) print(user_dict.popitem()) # ('c', 'lim3') print(user_dict) # OrderedDict([('b', 'lim1'), ('a', 'lim2')]) print(user_dict.pop('b')) # lim1 print(user_dict) user_dict.move_to_end('a') # 将key所对应的items移动到最后一个
ChainMap
Python3.3 的新功能,目的是为例将多个 dict 连接在一起,而且速度上会比多次调用 update()
快。
方便便利多个 dict 。
from collections import ChainMap user_dict1 = {'a': 'lim', 'b': 'lim1'} user_dict2 = {'aa': 'lim', 'bb': 'lim1'} new_dict = ChainMap(user_dict1, user_dict2) for key, value in new_dict.items(): print(key, value) # bb lim1 # aa lim # a lim # b lim1
可以将多个 dict 连接在一起,方便我们进行统一的操作。
如果有多个相同的 key 的话,就只会取第一个 key 的字典,后面的不会取。
这个模块并没有将参数中的dict的内容拷贝到一个新的数据结构里面,而是在其中添加了一个迭代器,方便我们进行操作。
0 条评论