• collections模块实现一些特定的数据类型,可以替代Python中常用的内置数据类型如dict, list, set,tuple,简单说就是对基本数据类型做了更上一层的处理。

  • collections是日常工作中的重点、高频模块,常用类型有:

    • 计数器(Counter)

    • 双向队列(deque)

    • 默认字典(defaultdict)

    • 有序字典(OrderedDict)

    • 具名元组(namedtuple) 

1. 计数器Counter

  • Counter作为字典dict的一个子类, 可以支持方便、快速的计数 ,将元素进行数量统计,计数后返回一个字典,键值为元素,值为元素个数
  • 常用方法

    方法名 描述
    most_common(int) 按照元素出现的次数进行从高到低的排序,返回前int个元素的字典
    elements 返回经过计算器Counter后的元素,返回的是一个迭代器
    update 和set集合的update一样,对集合进行并集更新
    subtract 和update类似,只是update是做加法,subtract做减法,从另一个集合中减去本集合的元素
    items 返回由Counter生成的字典的所有item
    keys 返回由Counter生成的字典的所有key
    values 返回由Counter生成的字典的所有value
  • 示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    from collections import Counter

    str = "abcabccabde"
    li = ['a', 'b', 'a', 'b', 'c']
    dic = {'a' : 2, 'b' : 2, 'c' : 3}

    # Counter对象获取各个元素的个数,返回字典
    print("Counter(str):", Counter(str))
    print("Counter(li):", Counter(li))
    print("Counter(dic):", Counter(dic))

    # most_common(int)按照元素出现的次数进行从高到低的排序,返回前int个元素的元组(键,值)
    d1 = Counter(str)
    print ("d1.most_common(2):",d1.most_common(2))
    d1 = Counter(str)
    print ("d1.most_common():",d1.most_common()) # 不加参数返回所有

    # elements返回经过计算器Counter后的元素,返回的是一个迭代器
    print ("sorted(d1.elements()):", sorted(d1.elements()))
    print ("list(d1.elements()):", list(d1.elements()))

    # update和set集合的update一样,对集合进行并集更新
    print ("d1.update('aaa'):", d1.update("aaa"))
    print(d1)

    # subtract 和update类似,只是update是做加法,subtract做减法,从另一个集合中减去本集合的元素
    print ("d1.substract('aab'):", d1.subtract("aab"))
    print(d1)

    # items 返回由Counter生成的字典的所有item
    print ("d1.items():", d1.items())

    # 返回由Counter生成的字典的所有key
    print ("d1.keys():", d1.keys())

    # 返回由Counter生成的字典的所有value
    print ("d1.values():", d1.values())

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ->Counter(str): Counter({'a': 3, 'b': 3, 'c': 3, 'd': 1, 'e': 1})
    ->Counter(li): Counter({'a': 2, 'b': 2, 'c': 1})
    ->Counter(dic): Counter({'c': 3, 'a': 2, 'b': 2})

    ->d1.most_common(2): [('a', 3), ('b', 3)]
    ->d1.most_common(): [('a', 3), ('b', 3), ('c', 3), ('d', 1), ('e', 1)]

    ->sorted(d1.elements()): ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'e']
    ->list(d1.elements()): ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'e']

    ->d1.update('aaa'): None
    ->Counter({'a': 6, 'b': 3, 'c': 3, 'd': 1, 'e': 1})

    ->d1.substract('aab'): None
    ->Counter({'a': 4, 'c': 3, 'b': 2, 'd': 1, 'e': 1})

    ->d1.items(): dict_items([('a', 4), ('b', 2), ('c', 3), ('d', 1), ('e', 1)])

    ->d1.keys(): dict_keys(['a', 'b', 'c', 'd', 'e'])

    ->d1.values(): dict_values([4, 2, 3, 1, 1])

2.双向队列deque

  • deque是栈和队列的一种广义实现,deque是”double-end queue”的简称;deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但是它主要在固定长度操作上的优化,从而在pop(0)和insert(0,v)(会改变数据的位置和大小)上有O(n)的时间复杂度。
  • 常用方法

    方法名 描述
    append 队列右边添加元素
    appendleft 队列左边添加元素
    clear 清空队列中的所有元素
    count(value) 返回队列中包含value的个数
    extend 队列右边扩展,可以是列表、元组或字典,如果是字典则将字典的key加入到deque
    extendleft 同extend,在左边扩展
    pop 移除并返回队列右边的元素
    popleft 移除并返回队列左边的元素
    remove(value) 移除队列第一个出现的元素
    reverse 队列的所有元素进行反转
    rotate(n) 对队列数进行移动
  • 示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    from collections import deque

    list1 = [0, 1, 2, 3]
    dst = deque(list1)
    print(dst)
    -> deque([0, 1, 2, 3])

    # append appendleft
    dst.append(4)
    dst.appendleft(-1)
    print(dst)
    ->deque([-1, 0, 1, 2, 3, 4])

    # extend extendleft
    ex = (1, 2)
    st = "abcd"
    dst.extend(ex)
    dst.extendleft(st)
    print(dst)
    ->deque(['d', 'c', 'b', 'a', -1, 0, 1, 2, 3, 4, 1, 2])

    # pop popleft
    dst.pop()
    dst.popleft()
    print(dst)
    ->deque(['c', 'b', 'a', -1, 0, 1, 2, 3, 4, 1])

    # count
    print(dst.count(2))
    ->1

    # insert
    dst.insert(0, '$')
    print(dst)
    ->deque(['$', 'c', 'b', 'a', -1, 0, 1, 2, 3, 4, 1])

    # rotate
    dst = deque([1, 2, 3, 4, 5], maxlen = 9)
    dst.rotate(2)
    print(dst)
    ->deque([4, 5, 1, 2, 3], maxlen=9)

    # remove
    dst.remove(1)
    print(dst)
    ->deque([4, 5, 2, 3], maxlen=9)

    # maxlen
    print(dst.maxlen)
    ->9

3. 默认字典defaultdict

  • Python中通过Key访问字典,当Key不存在时,会引发‘KeyError’异常。为了避免这种情况的发生,可以使用collections类中的defaultdict()方法来为字典提供默认值
  • defaultdict是内置数据类型dict的一个子类,基本功能与dict一样,只是重写了一个missing(key)和增加了一个可写的对象变量 default_factory

  • 如果default_factory属性为None,就报出以key作为遍历的KeyError异常;

  • 如果default_factory不为None,就会向给定的key提供一个默认值,这个值插入到词典中,并返回; 如 d = defaultdict(list) ,默认值是一个空的列表

  • 举例

    • default_factory属性为None

      1
      2
      3
      4
      # default_factory属性为None
      d = defaultdict()
      print(d['a'])
      # 报错:KeyError: 'a'
    • default_factory属性为list(列表字典)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      # default_factory属性为list
      d = defaultdict(list)
      print(d['a']) # 不存在键'a'时,返回默认值一个空的列表
      print(d) # 此时d中增加了一个有默认值的键了
      # ->[]
      # ->defaultdict(<class 'list'>, {'a': []})

      s = [('y', 1), ('b', 2), ('y', 3), ('b', 4), ('r', 1)]
      d = defaultdict(list)
      for k, v in s:
      d[k].append(v) # 如果不存在键K,则返回一个默认值[]给d[k]
      print(d)
      # ->defaultdict(<class 'list'>, {'y': [1, 3], 'b': [2, 4], 'r': [1]})

      # dict.setdefault()也可以实现相同的功能
      e = {}
      for k, v in s:
      e.setdefault(k, []).append(v)
      print(e)
      # ->{'y': [1, 3], 'b': [2, 4], 'r': [1]}
    • default_factory属性为set(集合字典)

      1
      2
      3
      4
      5
      6
      7
      # 将default_factory设置为set,使得defaultdict可以建立一个集合字典
      s = [('r', 1), ('b', 2), ('r', 3), ('b', 4), ('r', 1), ('b', 4)]
      d = defaultdict(set)
      for k, v in s:
      d[k].add(v) # 键k不存在则返回一个默认的空set,set类似list,但set中元素不能重复
      print(d)
      # ->defaultdict(<class 'set'>, {'r': {1, 3}, 'b': {2, 4}})
    • default_factory属性为int(计数)

      1
      2
      3
      4
      5
      6
      7
      8
      # 设置default_factory为int,使得defaultdict可以用于计数
      # 字符串中的字母第一次出现时,字典中没有该字母,default_factory函数调用int()为其提供一个默认值0,加法操作将计算出每个字母出现的次数。
      s = 'abidajiadgaji'
      d = defaultdict(int)
      for k in s:
      d[k] += 1
      print(d)
      # ->defaultdict(<class 'int'>, {'a': 4, 'b': 1, 'i': 3, 'd': 2, 'j': 2, 'g': 1})

4. 具名元组namedtuple

  • 普通元组的局限性: 不能为元组内部的数据进行命名,所以往往我们并不知道一个元组所要表达的意义

  • namedtuple: 引入了 collections.namedtuple 这个工厂函数,来构造一个带字段名的元组。具名元组的实例和普通元组消耗的内存一样多,因为字段名都被存在对应的类里面。这个类跟普通的对象实例比起来也要小一些,因为 Python 不会用 dict 来存放这些实例的属性。

  • 定义格式:collections.namedtuple(typename, field_names, verbose=False, rename=False)

    • typename:元组名称
    • field_names: 元组中元素的名称
    • rename: 如果元素名称中含有 python 的关键字,则必须设置为 rename=True
    • verbose: 默认就好
  • 创建命名元组并实例化

    1
    2
    3
    4
    5
    6
    7
    8
    # 创建一个具名元组,需要两个参数,一个是元组名,另一个是元组的各个字段名。
    # 后者可以是有多个字符串组成的可迭代对象,或者是有空格分隔开的字段名组成的字符串
    User = namedtuple('User', ['name', 'age', 'id'])
    User = namedtuple('User', 'name age id')
    # User是具名元组的变量名,'User'是具名元组的名称,一般相同

    zlx = User('zlx', '22', '123456')
    print(zlx)
  • 具名元组的特有属性

    • 类属性**_fields**: 包含这个类所有字段名的元组
    • 类方法 **_make(iterable)**:接受一个可迭代对象来生产这个类的实例
    • 实例方法 **_asdict()**:把具名元组以 collections.OrderedDict 的形式返回,可以利用它来把元组里的信息友好的展示出来
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 获取所有字段名
    print(zlx._fields)
    # -> ('name', 'age', 'id')

    # 获取各个字段的值
    print(zlx.name)
    print(zlx.age)
    print(zlx.id)
    # -> zlx
    # -> 22
    # -> 123456

    # 修改对象属性,注意要使用"_replace"方法
    zlx = zlx._replace(age=18)
    print(zlx)
    # -> User(name='zlx', age=18, id='123456')

    # 将User对象转换成字典,注意要使用"_asdict"
    print(zlx._asdict())
    # -> {'name': 'zlx', 'age': 18, 'id': '123456'}

5. 有序字典 OrderedDict

  • python中的字典dict是无序的,即迭代遍历(键,值)的顺序可能和插入的顺序不一致,因为它是按照hash来存储的。 常规dict并不跟踪插入顺序,迭代处理会根据键在散列表中存储的顺序来生成值。在OrderDict中则相反,它会记住元素插入的顺序,并在创建迭代器时使用这个顺序。

  • OrderedDict提供了一个有序的字典结构,内部维护这一个根据键插入顺序排序的双向链表,在迭代操作的时候保持了元素插入时的顺序。需要注意的是,一个OrderedDic的大小是一个普通字典的两倍,因为内部维护了另外一个链表,所有在数据量大的时候,如果不要求保持插入顺序,最好用普通字典。

  • 有序字典和普通字典的相等测试:有序字典是对顺序敏感的,即元素插入顺序不同,两个有序字典就不同;普通字典对顺序是不敏感的,即元素插入顺序不同,两个普通字典仍相同。

  • 有序字典除具有普通字典的原始方法外,还提供了和顺序相关的操作,popitem(last=True)以LIFO方式弹出元素(last=False则以FIFO方式);reversed(dict)返回一个逆序的orderedDict对象

  • from collections import OrderedDict
    d = {}
    d['a'] = 'A'
    d['b'] = 'B'
    d['c'] = 'C'
    for k, v in d.items():
        print(k, v)
    b = {}
    b['a'] = 'A'
    b['c'] = 'C'
    b['b'] = 'B'
    for k, v in d.items():
        print(k, v)
    print(d == b)   # True
    
    d = OrderedDict()
    d['a'] = 'A'
    d['b'] = 'B'
    d['c'] = 'C'
    for k, v in d.items():
        print(k, v)
    b = OrderedDict()
    b['a'] = 'A'
    b['c'] = 'C'
    b['b'] = 'B'
    for k, v in d.items():
        print(k, v)
    print(d == b)   # False
    
    print(d.popitem(last=True))