python 9

本文深入解析Python中的set集合、queue双端队列、heapq堆操作、ChainMap、Counter类、defaultdict、namedtuple、OrderedDict、itertools模块和functools模块的高级用法,涵盖数据结构原理、算法实现及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 set集合
set 集合有如下两个特征:
1.set 不记录元素的添加顺序。
2.元素不允许重复。

set 集合支持的如下几个运算符:

<=:相当于调用 issubset() 方法,判断前面的 set 集合是否为后面的 set 集合的子集合。

     >=:相当于调用 issuperset() 方法,判断前面的 set 集合是否为后面的 set 集合的父集合。
    -:相当于调用 difference() 方法,用前面的 set 集合减去后面的 set 集合的元素。
    &:相当于调用 intersection() 方法,用于获取两个 set 集舍的交集。
    ^:计算两个集合异或的结果,就是用两个集合的并集减去交集的元素。

它的集合运算方法都有两个版本:
1.交集运算:intersection() 和 intersection_update(),前者不改变集合本身,而是返回两个集合的交集;后者会通过交集运算改变第一个集合。
2.并集运算:union() 和 update(),前者不改变集合本身,而是返回两个集合的并集;后者会通过并集运算改变第一个集合。
3.减法运算:difference() 和 difference_update(),前者不改变集合本身,而是返回两个集合做咸法的结果;后者改变第一个集合。

二、queue(双端队列)模块及用法(无师自通)
deque 的方法基本都有两个版本,这就体现了它作为双端队列的特征。deque 的左边(left)就相当于它的队列头(front),右边(right)就相当于它的队列尾(rear):

append 和 appendleft:在 deque 的右边或左边添加元素,也就是默认在队列尾添加元素。
pop 和 popleft:在 deque 的右边或左边弹出元素,也就是默认在队列尾弹出元素。
extend 和 extendleft:在 deque 的右边或左边添加多个元素,也就是默认在队列尾添加多个元素。

deque 中的 clear() 方法用于清空队列:insert() 方法则是线性表的方法,用于在指定位置插入元素。

1.假如程序要把 deque 当成使用,则意味着只在一端添加、删除元素,因此调用 append 和 pop 方法即可。例如如下程序:

from collections import deque
stack = deque(('Kotlin', 'Python'))

# 元素入栈
stack.append('Erlang')
stack.append('Swift')
print('stack中的元素:' , stack)

# 元素出栈,后添加的元素先出栈
print(stack.pop())
print(stack.pop())
print(stack)

2.假如程序要把 deque 当成队列使用,则意味着一端只用来添加元素,另一端只用来删除元素,因此调用 append、popleft 方法即可。

from collections import deque
q = deque(('Kotlin', 'Python'))

# 元素加入队列
q.append('Erlang')
q.append('Swift')
print('q中的元素:' , q)

# 元素出队列,先添加的元素先出队列
print(q.popleft())
print(q.popleft())
print(q)

3.deque 还有一个 rotate() 方法

from collections import deque
q = deque(range(5))
print('q中的元素:' , q)
# 执行旋转,使之首尾相连
q.rotate()
print('q中的元素:' , q)
# 再次执行旋转,使之首尾相连
q.rotate()
print('q中的元素:' , q)

运行上面程序,可以看到如下输出结果:

q中的元素: deque([0, 1, 2, 3, 4])
q中的元素: deque([4, 0, 1, 2, 3])
q中的元素: deque([3, 4, 0, 1, 2])

三、 heapq(堆操作)用法详解
上面这些函数就是执行堆操作的工具函数,这些函数的功能大致如下:
heappush(heap, item):将 item 元素加入堆。
heappop(heap):将堆中最小元素弹出。

from heapq import *

heapify(heap):将堆属性应用到列表上。
heapreplace(heap, x):将堆中最小元素弹出,并将元素x 入堆。

heapify(my_data)

heappush(my_data, 7.2)

merge(*iterables, key=None, reverse=False):将多个有序的堆合并成一个大的有序堆,然后再输出。
多个有序序列从开始互相比较,输出最小。无法用于堆合并。
heappushpop(heap, item):将item 入堆,然后弹出并返回堆中最小的元素。

nlargest(n, iterable, key=None):返回堆中最大的 n 个元素。
nsmallest(n, iterable, key=None):返回堆中最小的 n 个元素。

print('my_data中最大的3个元素:', nlargest(3, my_data))
print('my_data中最小的4个元素:', nsmallest(4, my_data))

四、ChainMap用法
1.ChainMap 是一个方便的工具类,它使用链的方式将多个 dict“链”在一起,从而允许程序可直接获取任意一个 dict 所包含的 key 对应的 value。

2.由于 ChainMap 只是将多个 dict 链在一起,并未真正合并它们,因此在多个 dict 中完全可能具有重复的 key,在这种情况下,排在“链”前面的 dict 中的 key 具有更高的优先级。

from collections import ChainMap
# 定义3个dict对象
a = {'Kotlin': 90, 'Python': 86}
b = {'Go': 93, 'Python': 92}
c = {'Swift': 89, 'Go': 87}

# 将3个dict对象链在一起,就像变成了一个大的dict
cm = ChainMap(a, b , c)
print(cm)

# 获取Kotlin对应的value
print(cm['Kotlin']) # 90

# 获取Python对应的value
print(cm['Python']) # 86

# 获取Go对应的value
print(cm['Go']) # 93

五、 Counter类用法完全攻略
1.Counter 的本质就是一个特殊的 dict,只不过它的 key 都是其所包含的元素,而它的 value 则记录了该 key 出现的次数。

from collections import Counter
# 创建空的Counter对象
c1 = Counter()

# 以可迭代对象创建Counter对象
c2 = Counter('hannah')
print(c2)

# 以可迭代对象创建Counter对象
c3 = Counter(['Python', 'Swift', 'Swift', 'Python', 'Kotlin', 'Python'])
print(c3)

# 以dict来创建Counter对象
c4 = Counter({'red': 4, 'blue': 2})
print(c4)

# 使用关键字参数的语法创建Counter
c5 = Counter(Python=4, Swift=8)
print(c5)

运行上面程序,可以看到如下输出结果:

Counter({'h': 2, 'a': 2, 'n': 2})
Counter({'Python': 3, 'Swift': 2, 'Kotlin': 1})
Counter({'red': 4, 'blue': 2})
Counter({'Swift': 8, 'Python': 4})

2.事实上,Counter 继承了 dict 类,因此它完全可以调用 dict 所支持的方法。此外,Counter 还提供了如下三个常用的方法:
elements():该方法返回该 Counter 所包含的全部元素组成的迭代器。
most_common([n]):该方法返回 Counter 中出现最多的 【n 个元素】列表。

subtract([iterable-or-mapping]):该方法计算 Counter 的减法,其实就是计算减去之后各元素出现的次数。

from collections import Counter
# 创建Counter对象
cnt = Counter()
# 访问并不存在的key,将输出该key的次数为0.
print(cnt['Python']) # 0
for word in ['Swift', 'Python', 'Kotlin', 'Kotlin', 'Swift', 'Go']:
    cnt[word] += 1
print(cnt)

# 只访问Counter对象的元素
print(list(cnt.elements()))

# 将字符串(迭代器)转换成Counter
chr_cnt = Counter('abracadabra')

# 获取出现最多的3个字母
print(chr_cnt.most_common(3))  # [('a', 5), ('b', 2), ('r', 2)]
c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)

# 用Counter对象执行减法,其实就是减少各元素的出现次数
c.subtract(d)
print(c) # Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
e = Counter({'x': 2, 'y': 3, 'z': -4})

# 调用del删除key-value对,会真正删除该key-value对
del e['y']
print(e)

# 访问'w'对应的value,'w'没有出现过,因此返回0
print(e['w']) # 0

# 删除e['w'],删除该key-value对
del e['w']

# 再次访问'w'对应的value,'w'还是没有,因此返回0
print(e['w']) # 0
# 获取Counter中最少出现的3个元素
print(c.most_common()[:-4:-1]) # [('Go', -2), ('Swift', 2), ('Kotlin', 3)]

六、defaultdict用法(带实例分析)
1.defaultdict 是 dict 的子类,因此 defaultdict 也可被当成 dict 来使用,dict 支持的功能,defaultdict 基本都支持。但它与 dict 最大的区别在于,如果程序试图根据不存在的 key 采访问 dict 中对应的 value,则会引发 KeyError 异常;而 defaultdict 则可以提供一个 default_factory 属性,该属性所指定的函数负责为不存在的 key 来生成 value。

下面先使用普通 dict 来完成这项工作:

s = [('Python', 1), ('Swift', 2), ('Python', 3), ('Swift', 4), ('Python', 9)]
d = {}
for k, v in s:
    # setdefault()方法用于获取指定key对应的value.
    # 如果该key不存在,则先将该key对应的value设置为默认值:[]
    d.setdefault(k, []).append(v)
print(list(d.items()))


from collections import defaultdict
s = [('Python', 1), ('Swift', 2), ('Python', 3), ('Swift', 4), ('Python', 9)]
# 创建defaultdict,设置由list()函数来生成默认值
d = defaultdict(list)
for k, v in s:
    # 直接访问defaultdict中指定key对应的value即可。
    # 如果该key不存在,defaultdict会自动为该key生成默认值
    d[k].append(v)
print(list(d.items()))

七、namedtuple
namedtuple() 是一个工厂函数,使用该函数可以创建一个 tuple 类的子类,该子类可以为 tuple 的每个元素都指定宇段名,这样程序就可以根据字段名来访问 namedtuple 的各元素了

八、OrderedDict用法详解
1.其最大特征是,它可以**“维护”添加 key-value 对的顺序**。简单来说,就是先添加的 key-value 对排在前面,后添加的 key-value 对排在后面。

2.此外,由于 OrderedDict 是有序的,因此 Python 为之提供了如下两个方法:
1)popitem(last=True):默认弹出并返回最右边(最后加入)的 key-value 对;如果将 last 参数设为 False,则弹出并返回最左边(最先加入)的 key-value 对。
2)move_to_end(key, last=True):默认将指定的 key-value 对移动到最右边(最后加入);如果将 last 改为 False,则将指定的 key-value 对移动到最左边(最先加入)。

from collections import OrderedDict
d = OrderedDict.fromkeys('abcde')
# 将b对应的key-value对移动到最右边(最后加入)
d.move_to_end('b')
print(d.keys()) # odict_keys(['a', 'c', 'd', 'e', 'b'])

# 将b对应的key-value对移动到最左边(最先加入)
d.move_to_end('b', last=False)
print(d.keys()) # odict_keys(['b', 'a', 'c', 'd', 'e'])

# 弹出并返回最右边(最后加入)的key-value对
print(d.popitem()[0]) # e

# 弹出并返回最左边(最先加入)的key-value对
print(d.popitem(last=False)[0]) # b

九、 itertools模块:生成迭代器

先看 itertools 模块中三个生成无限迭代器的函数:
1count(start, [step]):生成 start, start+step, start+2*step,… 的迭代器,其中 step 默认为 1。比如使用 count(10) 生成的迭代器包含:10, 11 , 12 , 13, 14,…。

2.cycle§:对序列 p 生成无限循环 p0, p1,…, p0, p1,… 的迭代器。比如使用 cycle(‘ABCD’) 生成的迭代器包含:A,B,C,D,A,B,C,D,…。

3.repeat(elem [,n]):生成无限个 elem 元素重复的迭代器,如果指定了参数 n,则只生成 n 个 elem 元素。比如使用 repeat(10, 3) 生成的法代器包含:10, 10, 10。

import itertools as it
# count(10, 3)生成10、13、16……迭代器

for e in it.count(10, 3):
    print(e)
    # 用于跳出无限循环
    if e > 20:
        break
print('---------')
my_counter = 0

# cycle用于对序列生成无限循环的迭代器
for e in it.cycle(['Python', 'Kotlin', 'Swift']):
    print(e)
    # 用于跳出无限循环
    my_counter += 1
    if my_counter > 7:
        break
print('---------')

# repeat用于生成n个元素重复的迭代器
for e in it.repeat('Python', 3):
    print(e)

在 itertools 模块中还有一些常用的迭代器函数,如下所示:
accumulate(p[,func]):默认生成根据序列 p 元素累加的迭代器,p0, p0+p1, p0+p1+p2,…序列,如果指定了 func 函数,则用 func 函数来计算下一个元素的值。
chain(p, q, …):将多个序列里的元素“链”在一起生成新的序列。
compress(data, selectors):根据 selectors 序列的值对 data 序列的元素进行过滤。如果 selector[0] 为真,则保留 data[0];如果 selector[1] 为真,则保留 data[1]…依此类推。
dropwhile(pred, seq):使用 pred 函数对 seq 序列进行过滤,从 seq 中第一个使用 pred 函数计算为 False 的元素开始,保留从该元素到序列结束的全部元素。
takewhile(pred, seq):该函数和上一个函数恰好相反。使用 pred 函数对 seq 序列进行过滤,从 seq 中第一个使用 pred 函数计算为 False 的元素开始,去掉从该元素到序列结束的全部元素。
filterfalse(pred, seq):使用 pred 函数对 seq 序列进行过滤,保留 seq 中使用 pred 计算为 True 的元素。比如 filterfalse(lambda x:x%2, range(10)),得到 0, 2, 4, 6, 8。
islice(seq, [start,] stop [, step]):其功能类似于序列的 slice 方法,实际上就是返回 seq[start:stop:step] 的结果。
starmap(func, seq):使用 func 对 seq 序列的每个元素进行计算,将计算结果作为新的序列元素。当使用 func 计算序列元素时,支持序列解包。比如 seq 序列的元素长度为 3,那么 func 可以是一个接收三个参数的函数,该函数将会根据这三个参数来计算新序列的元素。
zip_longest(p,q,…):将 p、q 等序列中的元素按索引合并成元组,这些元组将作为新序列的元素。

import itertools as it
# 默认使用累加的方式计算下一个元素的值
for e in it.accumulate(range(6)):
    print(e, end=', ') # 0, 1, 3, 6, 10, 15
print('\n---------')
# 使用x*y的方式来计算迭代器下一个元素的值
for e in it.accumulate(range(1, 6), lambda x, y: x * y):
    print(e, end=', ') # 1, 2, 6, 24, 120
print('\n---------')
# 将两个序列“链”在一起,生成新的迭代器
for e in it.chain(['a', 'b'], ['Kotlin', 'Swift']):
    print(e, end=', ') # 'a', 'b', 'Kotlin', 'Swift'
print('\n---------')
# 根据第二个序列来筛选第一个序列的元素,
# 由于第二个序列只有中间两个元素为1(True),因此前一个序列只保留中间两个元素
for e in it.compress(['a', 'b', 'Kotlin', 'Swift'], [0, 1, 1, 0]):
    print(e, end=', ') # 只有: 'b', 'Kotlin'
print('\n---------')
# 获取序列中从长度不小于4的元素开始、到结束的所有元素
for e in it.dropwhile(lambda x:len(x)<4, ['a', 'b', 'Kotlin', 'x', 'y']):
    print(e, end=', ') # 只有: 'Kotlin', 'x', 'y'
print('\n---------')
# 去掉序列中从长度不小于4的元素开始、到结束的所有元素
for e in it.takewhile(lambda x:len(x)<4, ['a', 'b', 'Kotlin', 'x', 'y']):
    print(e, end=', ')  # 只有: 'a', 'b'
print('\n---------')
# 只保留序列中从长度不小于4的元素
for e in it.filterfalse(lambda x:len(x)<4, ['a', 'b', 'Kotlin', 'x', 'y']):
    print(e, end=', ') # 只有: 'Kotlin'
print('\n---------')
# 使用pow函数对原序列的元素进行计算,将计算结果作为新序列的元素
for e in it.starmap(pow, [(2,5), (3,2), (10,3)]):
    print(e, end=', ') # 32, 9, 1000
print('\n---------')
# 将'ABCD'、'xy'的元素按索引合并成元组,这些元组作为新序列的元素
# 长度不够的序列元素使用'-'字符代替
for e in it.zip_longest('ABCD', 'xy', fillvalue='-'):
    print(e, end=', ') # ('A', 'x'), ('B', 'y'), ('C', '-'), ('D', '-')

运行上面程序,可以看到如下输出结果:

0, 1, 3, 6, 10, 15,
---------
1, 2, 6, 24, 120,
---------
a, b, Kotlin, Swift,
---------
b, Kotlin,
---------
Kotlin, x, y,
---------
a, b,
---------
Kotlin,
---------
32, 9, 1000,
---------
('A', 'x'), ('B', 'y'), ('C', '-'), ('D', '-'),

在 itertools 模块中还有一些用于生成排列组合的工具函数:
product(p, q, …[repeat= 1)]:用序列 p 、q 、… 中的元素进行排列组合,就相当于使用嵌套循环组合。
permutations(p[, r]):从序列 p 中取出 r 个元素组成全排列,将排列得到的元组作为新迭代器的元素。
combinations(p, r):从序列 p 中取出 r 个元素组成全组合,元素不允许重复,将组合得到的元组作为新迭代器的元素。
combinations with_replacement(p, r),从序列 p 中取出 r 个元素组成全组合,元素允许重复,将组合得到的元组作为新迭代器的元素。

十、 functools模块完全攻略
在functools 模块中常用的函数装饰器和功能函数如下:
functools.cmp_to_key(func):将老式的比较函数(func)转换为关键字函数(key function)。在 Python 3 中比较大小、排序都是基于关键字函数的,Python 3 不支持老式的比较函数。
@functools.lru_cache(maxsize=128, typed=False):该函数装饰器使用 LRU(最近最少使用)缓存算法来缓存相对耗时的函数结果,避免传入相同的参数重复计算。同时,缓存并不会无限增长,不用的缓存会被释放。其中 maxsize 参数用于设置缓存占用的最大字节数,typed 参数用于设置将不同类型的缓存结果分开存放。
@functools.total_ordering:这个类装饰器(作用类似于函数装饰器,只是它用于修饰类)用于为类自动生成比较方法。通常来说,开发者只要提供 lt()、le()、gt()、ge() 其中之一(最好能提供 eq() 方法),@functools.total_ordering装饰器就会为该类生成剩下的比较方法。
functools.partial(func, *args, **keywords):该函数用于为 func 函数的部分参数指定参数值,从而得到一个转换后的函数,程序以后调用转换后的函数时,就可以少传入那些己指定值的参数。
functools.partialmethod(func, *args, **keywords):该函数与上一个函数的含义完全相同,只不过该函数用于为类中的方法设置参数值。
functools.reduce(function, iterable[, initializer]):将初始值(默认为 0,可由 initializer 参数指定)、迭代器的当前元素传入 function 函数,将计算出来的函数结果作为下一次计算的初始值、迭代器的下一个元素再次调用 function 函数……依此类推,直到迭代器的最后一个元素。
@functools.singledispatch:该函数装饰器用于实现函数对多个类型进行重载。比如同样的函数名称,为不同的参数类型提供不同的功能实现。该函数的本质就是根据参数类型的变换,将函数转向调用不同的函数。
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):对 wrapper 函数进行包装,使之看上去就像 wrapped(被包装)函数。
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):该函数装饰器用于修饰包装函数,使包装函数看上去就像 wrapped 函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值