python标准库学习(1):collections

您所在的位置:网站首页 python判断元素是否在list中 python标准库学习(1):collections

python标准库学习(1):collections

2023-02-20 05:45| 来源: 网络整理| 查看: 265

python标准库学习(1) 标准库collections 2019-08-07

标准库collections为python在列表(list), 元组(tuple), 字典(dict)等基础上,增加了几种数据类型,这些类型可以视作是对原有的几种数据类型的扩展:

数据类型 作用 namedtuple 可以用属性名称来访问的元组 deque 双向(前后)队列 Counter 可以用属性名称来访问的元组 OrderedDict 有序字典 defaultdict 有默认值的字典 ChainMap 将多个字典合并在一个映射中

除以上这些数据类型以外,还有UserDict、UserList、UserString三个抽象基类,涉及这部分的暂不讨论。

1 collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)

namedtuple,可以称为具名元组或命名元组,是对元组(tuple)的继承和扩展,被称为是一个“工厂函数”(factory function)。一般而言,元组中的元素(item)只能通过索引(index)进行访问,例如:

a_tuple = ('a', 'b', 'c', 2, 3, 4) s = a_tuple[-1] # 结果是 4

namedtuple扩展了另一种方式,使得元组中的元素可以通过名称(name)的方式访问到,这样做的好处是在利用元组储存数据时,每个元素的含义就很清楚,例如可以创建一个名为Staff的namedtuple:

from collections import namedtuple Staff=namedtuple("Staff",['name','age','email']) mars = Staff('Mars', 30, 'mars03@*****.com') june = Staff('June', 27, 'june_hcs@*****.com') june.email #结果为'june_hcs@*****.com'

可以发现这与定义一个名为Staff的类,构造函数(constructor,__init__)中定义name等3个属性类似。 这也是其被称为工厂函数的一个原因。 namedtuple中封装了3个比较有用的函数:_make,_replace和_asdict。_make可以将普通的元组转为一个namedtuple:

# 将一个普通元组转为Staff kar = ('Kar',31,'kcar@****.com') kar_nmdtp = Staff._make(kar) # Staff(name='Kar', age=31, email='kcar@****.com')

_replace允许使用属性名(name)来替换元组中的值:

kar = Staff('Kar',31,'kcar@****.com') kar_new = kar._replace(email='kar03@****.com') # 注意,kar这个元组中email的值不会被修改

_asdict会将一个namedtuple转为一个OrderedDict对象:

kar = Staff('Kar',31,'kcar@****.com') kar._asdict() # OrderedDict([('name', 'Kar'), ('age', 31), ('email', 'kcar@****.com')]) 2 collections.deque([iterable[, maxlen]])

deque是双边队列(double-ended queue)的缩写,参数maxlen指定了这个列表最大的长度。 既然称为“双边队列”,那么对应地,列表的插入、抛出等操作就可以扩展至两边,即增加一组带有left的操作:

from collections import deque dlst = deque(['a','b','c','d','e','f','g']) dlst.append('h') # deque(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']) dlst.appendleft('0') # deque(['0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']) dlst.pop() # deque(['0', 'a', 'b', 'c', 'd', 'e', 'f', 'g']) dlst.popleft() # deque(['a', 'b', 'c', 'd', 'e', 'f', 'g']) dlst.extend(['h','i']) # deque(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) dlst.extendleft(['0','1']) # deque(['1', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) dlst.rotate(2) # deque(['h', 'i', '1', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g'])

最后一个rotate(n)方法的作用是,当n ;;> 0时,将末尾n个元素移到开头,反之当n ;;< 0 时则将头部的n个元素移至末尾。 参数maxlen可以用来指定列表最大长度,当达到这个最大长度时,超越长度的元素将被从列表中移除:

from collections import deque dlst = deque(['a','b','c','d','e'], maxlen=8) dlst.extend(i for i in 'hijklmn') # deque(['e', 'h', 'i', 'j', 'k', 'l', 'm', 'n']) 3 collections.Counter([iterable-or-mapping])

Counter是一个带有计数功能的容器类,是字典(dict)的一个子类,对于可哈希对象,Counter统计元素的次数。下面这个例子会将字符串中每个字符出现的次数进行统计,返回一个字典

from collections import Counter s_str = '''It was the best of times, \ it was the worst of times, \ it was the age of wisdom, \ it was the age of foolishness, \ it was the epoch of belief,\ it was the epoch of incredulity, \ it was the season of Light,it was the season of Darkness, \ it was the spring of hope, it was the winter of despair,\ we had everything before us,we had nothing before us, \ we were all going direct to Heaven, \ we were all going direct the other way--in short,\ the period was so far like the present period, \ that some of its noisiest authorities insisted on its being received,\ for good or for evil, in the superlative degree of comparison only.''' # 这里先去掉字符串中的标点、空格,再统计字母出现的频数 s_str = s_str.replace(' ','').replace(',','').replace('.','').replace('-','') r = Counter(s_str) # Counter({'e': 69, 't': 48, 'o': 44, 'i': 44, 's': 42, 'a': 28, # 'h': 27, 'r': 27, 'n': 22, 'w': 21, 'f': 19, 'g': 13, 'd': 13, # 'l': 11, 'p': 10, 'c': 7, 'b': 5, 'm': 5, 'u': 5, 'v': 5, # 'y': 4, 'k': 2, 'I': 1, 'L': 1, 'D': 1, 'H': 1})

Counter提供了一些很有用的函数,其中一个是most_common(n=None),用于统计出现频次最高的1个(默认)或几个(指定n)元素:

# 接上面的例子 r.most_common(2) # [('e', 69), ('t', 48)],注意这是个列表,其中的元素是元组

elements()返回一个可迭代对象,将元素按照出现次数进行迭代:

v_str = 'Each thing, as far as it can by its own power, strives to persevere in its being' v_cnt = Counter(v_str.replace(' ','').replace(',','')) sorted(v_cnt.elements()) # 返回一个列表,将v_str中元素按顺序迭代出现的次数

参考源代码可以发现,在定义Counter类时,还封装了__add__、__sub__、__iadd__、__isub__、__and__、__or__等几个特殊方法,使得Counter对象之间可以实现类似加减、并、交的计算(这里直接引用源代码中的示例):

# 对结果进行加减 Counter('abbb') + Counter('bcc') # Counter({'b': 4, 'c': 2, 'a': 1}) Counter('abbbc') - Counter('bccd') # Counter({'a': 1, 'b': 2}) c = Counter('abbbc') c -= Counter('bccd') # Counter({'b': 2, 'a': 1}) # 并、交运算 Counter('abbb') | Counter('bcc') # Counter({'b': 3, 'c': 2, 'a': 1}) Counter('abbb') & Counter('bcc') # Counter({'b': 1}) 4 collections.OrderedDict(dict)

OrderedDict在定义字典的同时,维护了一个记录元素插入顺序的链表,使得字典成为有顺序的,正常迭代会遵循LIFO(先进后出)的顺序:

from collections import OrderedDict a_odct = OrderedDict() a_odct['a'] = 'a101' a_odct['b'] = 'a102' a_odct['c'] = 'a103' for k, v in a_odct.items(): print(k, ':', v) # a : a101 # b : a102 # c : a103

因为OrderedDict中字典有了排序,因此可以像列表一样进行堆栈操作,利用popitem(last=True)可以实现:

a_odct = OrderedDict() a_odct['a'] = 'a101' a_odct['b'] = 'a102' a_odct['c'] = 'a103' a_odct['d'] = 'b101' a_odct.popitem(last=False) # ('a', 'a101')

popitem()在last参数默认为True,可以实现LIFO(先进后出,Last In First Out),设为False时,可以实现FIFO(先进先出, First Input First Output)。 另一个方法move_to_end(key, last=True),可以通过制定键将一个键值对移到头部或尾部,last默认参数为True,即将制定的元素移到尾部:

a_odct = OrderedDict() a_odct['a'] = 'a101' a_odct['b'] = 'a102' a_odct['c'] = 'a103' a_odct['d'] = 'b101' a_odct.move_to_end('b') # 原字典键变为 OrderedDict([('a', 'a101'), ('c', 'a103'), ('d', 'b101'), ('b', 'a102')]) 5 collections.defaultdict([default_factory[, …]])

与namedtuple类似,defaultdict也是一个工厂函数,对于普通字典而言,当键不存在时,会发生KeyError:

from collections import defaultdict a_dct = {'a':'a101', 'b':'a102','c':'a103', 'd':'b101'} a_dct['r'] # KeyError: 'r'

对于不存在的键,可以通过get()方法设置默认值。defaultdict提供了另一种方式来解决KeyError的问题,即为字典设置默认值,当键不存在时返回一个默认的结果。defaultdict是dict的一个子类,在初始化时需要提供一个类型或者不带参数的可调用函数作为参数,如果是一个函数,那么defaultdict字典的默认值将会是函数的返回值:

from collections import defaultdict a_dft = defaultdict(int) a_dft['a'] # 0,类似地,如果传入的参数是list,将会返回[] from random import randint from collections import defaultdict a_dfd = defaultdict(lambda: randint(0, 100)) # 注意这里作为传入参数的匿名函数,没有参数 a_dfd['r'] = 23 a_dfd['s'] = 41 a_dfd['a'] # 会返回一个0和100之间的随机整数

以下这个统计次数的例子有助于更好理解defaultdict的作用,这也是其经常被与Counter做对比的原因。

cnt_lst = ['c','b','a','c','d','a','d','c','d','a'] # 这种方式会返回KeyError,因为字典中没有c这个键 cnt = {} for cr in cnt_lst: cnt[cr] +=1 # 这种方式稍改进了上面的逻辑,增加了对键是否存在的判断 cnt = {} for cr in cnt_lst: if cr not in cnt: cnt[cr] = 1 else: cnt[cr] += 1 cnt # 利用字典中setdefault方法也能实现,dict.setdefault(key, default=None)方法,如果key存在则返回值,如果不存在则设为默认值 cnt = {} for cr in cnt_lst: cnt[cr] = cnt.setdefault(cr, 0) + 1 # 如果一开始就将cnt设为一个defaultdict,则可以很简单实现 cnt = defaultdict(int) for cr in cnt_lst: cnt[cr] += 1 6 collections.ChainMap(*maps)

ChainMap可以称为“组合字典”,根据官方文档的介绍,“一个 ChainMap 类是为了将多个映射快速的链接到一起,这样它们就可以作为一个单元处理。它通常比创建一个新字典和多次调用 update() 要快很多。”ChainMap可以接收任意多个字典并将这些字典“合”在一起,之所以用加引号的合(并)来说ChainMap的作用,是因为本质上它并没有把几个字典做合并,而是建立了一个映射关系,这样既不会改变原有字典的内容,又能比较迅速的实现合并操作:

from collections import ChainMap a_dct = {'a':1, 'b':2} b_dct = {'c':3, 'd':4} c_dct = {'a':5, 'c':6} r = ChainMap(a_dct, b_dct, c_dct) # ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}) print(r['b']) # 2 print(r['a']) # 1

最后一个r['a']的例子可以发现,在键(key)存在相同的情况时,ChainMap会从根据自己的顺序找到一个值。 下面来看改变所映射的字典会对构建的ChainMap产生什么影响:

from collections import ChainMap a_dct = {'a':1, 'b':2} b_dct = {'c':3, 'd':4} c_dct = {'a':5, 'c':6} r = ChainMap(a_dct, b_dct, c_dct) # ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}) r['s'] = 11 # ChainMap({'a': 1, 'b': 2, 's': 11}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}),此时a_dct将被改变成为{'a': 1, 'b': 2, 's': 11} b_dct.pop('c') # ChainMap({'a': 1, 'b': 2, 's': 11}, {'d': 4}, {'a': 5, 'c': 6}) c_dct.update({'c':9}) # ChainMap({'a': 1, 'b': 2, 's': 11}, {'d': 4}, {'a': 5, 'c': 9})

可以发现,如果改变原字典,则ChainMap会相应发生变化;如果对ChainMap添加键值对,则这一组数据将被添加在ChainMap的第一个字典中,并会改变原有的字典数据。ChainMap有3个方法:map、new_child(m=None)、parents。map返回“一个可以更新的映射列表。这个列表是按照第一次搜索到最后一次搜索的顺序组织的。它是仅有的存储状态,可以被修改。”

from collections import ChainMap a_dct = {'a':1, 'b':2} b_dct = {'c':3, 'd':4} c_dct = {'a':5, 'c':6} r = ChainMap(a_dct, b_dct, c_dct) # ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}) print(r.maps) # [{'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}] r.maps.append({'w':3}) print(r) # ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}, {'w': 3})

new_child(m=None)则实现了“返回一个新的ChainMap类,包含了一个新映射(map),后面跟随当前实例的全部映射(map)。如果 m 被指定,它就成为不同新的实例,就是在所有映射前加上 m,如果没有指定,就加上一个空字典,这样的话一个 d.new_child()调用等价于 ChainMap({}, *d.maps)。这个方法用于创建子上下文,不改变任何父映射的值。”

from collections import ChainMap a_dct = {'a':1, 'b':2} b_dct = {'c':3, 'd':4} c_dct = {'a':5, 'c':6} r = ChainMap(a_dct, b_dct, c_dct) # ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}) s = r.new_child() # s将是ChainMap({}, {'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}),r则不会发生改变 s['w'] = 12 print(s) # ChainMap({'w': 12}, {'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6})

如果参数m被指定,则它会被添加到ChainMap的最前面。

from collections import ChainMap a_dct = {'a':1, 'b':2} b_dct = {'c':3, 'd':4} c_dct = {'a':5, 'c':6} r = ChainMap(a_dct, b_dct, c_dct) # ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}) s = r.new_child(c_dct) # ChainMap({'a': 5, 'c': 6}, {'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}),r则不会发生改变

parents“属性返回一个新的 ChainMap包含所有的当前实例的映射,除了第一个。这样可以在搜索的时候跳过第一个映射。 ”

from collections import ChainMap a_dct = {'a':1, 'b':2} b_dct = {'c':3, 'd':4} c_dct = {'a':5, 'c':6} r = ChainMap(a_dct, b_dct, c_dct) # ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 5, 'c': 6}) s = r.parents # s将成为ChainMap({'c': 3, 'd': 4}, {'a': 5, 'c': 6}),r则不会发生改变 v = r.parents.parents # v将变成ChainMap({'a': 5, 'c': 6}), r和s不会发生变化


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3