Collection 模块中的各种方法和使用,基于 Python3 的版本学习。这个模块实现了基于 Python 中的一些基础模块,如 dict, list, tuple 和 set,的上层应用,在基础模块上增加了很多方法,在实际使用的过程中也显得简单方便,更有实践性。

参考:

函数作用
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中的内容,但是并没有改变这个listid,所以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 条评论

发表回复

您的电子邮箱地址不会被公开。