Python 标准库高级用法:itertools
, functools
, collections
精讲
大家好,今天我们深入探讨 Python 标准库中三个非常强大的模块:itertools
、functools
和 collections
。 它们提供了许多高级的工具和数据结构,可以极大地简化代码,提高效率,并实现一些原本比较复杂的功能。
一、itertools
:迭代器工具箱
itertools
模块提供了一系列用于创建迭代器的函数。 这些函数可以用于构建复杂的迭代器链,进行高效的数据处理。 迭代器是 Python 中一个重要的概念,它允许我们逐个访问序列中的元素,而无需一次性将整个序列加载到内存中。
1. 无限迭代器
itertools
提供了一些可以无限生成的迭代器,需要谨慎使用,避免无限循环。
-
count(start=0, step=1)
: 生成一个从start
开始,以step
为步长的无限序列。import itertools counter = itertools.count(start=5, step=2) for _ in range(5): print(next(counter)) # 输出: 5, 7, 9, 11, 13
-
cycle(iterable)
: 无限循环一个可迭代对象。colors = itertools.cycle(['red', 'green', 'blue']) for _ in range(10): print(next(colors)) # 输出: red, green, blue, red, green, blue, ...
-
repeat(object, times=None)
: 重复生成一个对象times
次。 如果times
为None
,则无限重复。repeater = itertools.repeat('Hello', times=3) for item in repeater: print(item) # 输出: Hello, Hello, Hello # 无限重复 # infinite_repeater = itertools.repeat('Infinite') # 谨慎使用
2. 组合迭代器
这些函数用于从一个或多个可迭代对象中生成组合或排列。
-
*`chain(iterables)`**: 将多个可迭代对象连接成一个迭代器。
list1 = [1, 2, 3] list2 = ['a', 'b', 'c'] chained = itertools.chain(list1, list2) print(list(chained)) # 输出: [1, 2, 3, 'a', 'b', 'c']
-
compress(data, selectors)
: 使用selectors
中的布尔值来过滤data
中的元素。data = [1, 2, 3, 4, 5] selectors = [True, False, True, False, True] compressed = itertools.compress(data, selectors) print(list(compressed)) # 输出: [1, 3, 5]
-
dropwhile(predicate, iterable)
: 从iterable
中丢弃元素,直到predicate
返回False
。numbers = [1, 4, 6, 4, 1] dropped = itertools.dropwhile(lambda x: x < 5, numbers) print(list(dropped)) # 输出: [6, 4, 1]
-
takewhile(predicate, iterable)
: 从iterable
中获取元素,直到predicate
返回False
。numbers = [1, 4, 6, 4, 1] taken = itertools.takewhile(lambda x: x < 5, numbers) print(list(taken)) # 输出: [1, 4]
-
filterfalse(predicate, iterable)
: 过滤iterable
中的元素,只保留predicate
返回False
的元素。numbers = [1, 2, 3, 4, 5] filtered = itertools.filterfalse(lambda x: x % 2 == 0, numbers) print(list(filtered)) # 输出: [1, 3, 5]
-
groupby(iterable, key=None)
: 将iterable
中的元素按key
函数的结果进行分组。data = [("a", 1), ("a", 2), ("b", 3), ("b", 4), ("c", 5)] grouped = itertools.groupby(data, key=lambda x: x[0]) for key, group in grouped: print(key, list(group)) # 输出: # a [('a', 1), ('a', 2)] # b [('b', 3), ('b', 4)] # c [('c', 5)]
-
islice(iterable, start, stop, step=1)
: 从iterable
中切片,类似于列表切片。numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] sliced = itertools.islice(numbers, 2, 7, 2) print(list(sliced)) # 输出: [2, 4, 6]
-
tee(iterable, n=2)
: 从一个迭代器创建n
个独立的迭代器。numbers = [1, 2, 3] it1, it2 = itertools.tee(numbers, 2) print(list(it1)) # 输出: [1, 2, 3] print(list(it2)) # 输出: [1, 2, 3] # 注意:`tee` 创建的迭代器会共享底层数据源。如果其中一个迭代器消耗了部分数据,其他迭代器将无法访问这些数据。
-
*`zip_longest(iterables, fillvalue=None)
**: 类似于
zip,但如果可迭代对象的长度不同,则使用
fillvalue` 填充。list1 = [1, 2, 3] list2 = ['a', 'b'] zipped = itertools.zip_longest(list1, list2, fillvalue='-') print(list(zipped)) # 输出: [(1, 'a'), (2, 'b'), (3, '-')]
3. 排列组合迭代器
这些函数用于生成排列、组合和笛卡尔积。
-
*`product(iterables, repeat=1)`**: 计算可迭代对象的笛卡尔积。
colors = ['red', 'green'] sizes = ['S', 'M', 'L'] combinations = itertools.product(colors, sizes) print(list(combinations)) # 输出: [('red', 'S'), ('red', 'M'), ('red', 'L'), ('green', 'S'), ('green', 'M'), ('green', 'L')] # repeat 参数 cards = ['A', 'B', 'C'] repeated_product = itertools.product(cards, repeat=2) print(list(repeated_product)) # 输出: [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
-
permutations(iterable, r=None)
: 生成可迭代对象的所有长度为r
的排列。 如果r
为None
,则使用可迭代对象的长度。letters = ['A', 'B', 'C'] permutations_list = itertools.permutations(letters) print(list(permutations_list)) # 输出: [('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')] permutations_length_2 = itertools.permutations(letters, r=2) print(list(permutations_length_2)) # 输出: [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
-
combinations(iterable, r)
: 生成可迭代对象的所有长度为r
的组合(不考虑顺序)。letters = ['A', 'B', 'C', 'D'] combinations_list = itertools.combinations(letters, r=2) print(list(combinations_list)) # 输出: [('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
-
combinations_with_replacement(iterable, r)
: 生成可迭代对象的所有长度为r
的组合,允许重复元素。letters = ['A', 'B', 'C'] combinations_with_replacement_list = itertools.combinations_with_replacement(letters, r=2) print(list(combinations_with_replacement_list)) # 输出: [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
二、functools
:高阶函数工具
functools
模块提供了一系列用于操作函数的工具,包括函数柯里化、缓存、重载等。 它主要用于编写更简洁、可重用的函数。
1. partial
:函数柯里化
partial
允许我们创建一个新的函数,该函数预先填充了原始函数的一些参数。
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2) # 创建一个计算平方的函数
cube = partial(power, exponent=3) # 创建一个计算立方的函数
print(square(5)) # 输出: 25
print(cube(5)) # 输出: 125
# 也可以绑定位置参数
multiply = lambda x, y: x * y
double = partial(multiply, 2)
triple = partial(multiply, y=3)
print(double(5)) # 输出 10
print(triple(5)) # 输出 15
2. lru_cache
:缓存
lru_cache
是一个装饰器,用于缓存函数的返回值。 当函数使用相同的参数再次调用时,将直接从缓存中返回结果,而无需重新计算。 lru_cache
使用 LRU (Least Recently Used) 算法来管理缓存,自动删除最近最少使用的结果。
from functools import lru_cache
import time
@lru_cache(maxsize=32) # 缓存最近 32 个结果
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
start = time.time()
print(fibonacci(35))
end = time.time()
print(f"耗时: {end - start:.4f} 秒")
start = time.time()
print(fibonacci(35)) # 从缓存中读取
end = time.time()
print(f"耗时: {end - start:.4f} 秒")
print(fibonacci.cache_info()) # 查看缓存信息
# cache_info(hits=1, misses=36, maxsize=32, currsize=32)
fibonacci.cache_clear() # 清空缓存
3. reduce
:累积计算
虽然 reduce
现在通常被认为不太 Pythonic (推荐使用循环或列表推导式),但了解它仍然是有用的。 reduce(function, iterable[, initializer])
将 function
累积地应用于 iterable
的元素,从左到右,以将序列减少到单个值。
from functools import reduce
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 输出: 120
# 使用 initializer
sum_of_squares = reduce(lambda x, y: x + y**2, numbers, 0)
print(sum_of_squares) # 输出 55
4. wraps
:保留函数元信息
wraps
是一个装饰器,用于更新包装函数的元信息(如 __name__
、__doc__
等),使其与被包装函数保持一致。这对于编写装饰器非常重要,可以避免装饰器改变原始函数的行为。
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Wrapper function docstring."""
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@my_decorator
def say_hello(name):
"""Say hello function docstring."""
return f"Hello, {name}!"
print(say_hello.__name__) # 输出: say_hello
print(say_hello.__doc__) # 输出: Say hello function docstring.
print(say_hello("World"))
# 输出:
# Before function call
# After function call
# Hello, World!
5. total_ordering
:简化比较操作
total_ordering
是一个类装饰器,用于自动生成类的所有比较方法(__lt__
, __le__
, __gt__
, __ge__
),只需要定义 __eq__
和 __lt__
方法即可。
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __eq__(self, other):
return self.grade == other.grade
def __lt__(self, other):
return self.grade < other.grade
def __repr__(self):
return f"Student(name={self.name}, grade={self.grade})"
student1 = Student("Alice", 85)
student2 = Student("Bob", 90)
student3 = Student("Charlie", 85)
print(student1 < student2) # 输出: True
print(student1 > student2) # 输出: False
print(student1 <= student3) # 输出: True
print(student1 >= student3) # 输出: True
print(student1 == student3) # 输出: True
print(student1 != student2) # 输出: True
三、collections
:集合工具
collections
模块提供了一些特殊的数据结构,可以解决 Python 内置数据结构的一些局限性。
1. Counter
:计数器
Counter
是一个字典的子类,用于统计可迭代对象中元素的出现次数。
from collections import Counter
words = ['red', 'blue', 'red', 'green', 'blue', 'blue']
word_counts = Counter(words)
print(word_counts) # 输出: Counter({'blue': 3, 'red': 2, 'green': 1})
# most_common(n) 方法返回出现次数最多的前 n 个元素
print(word_counts.most_common(2)) # 输出: [('blue', 3), ('red', 2)]
# elements() 方法返回一个迭代器,包含所有元素的重复副本
print(list(word_counts.elements())) # 输出: ['red', 'red', 'blue', 'blue', 'blue', 'green']
# 可以进行数学运算
more_words = ['red', 'yellow']
word_counts.update(more_words)
print(word_counts) # Counter({'blue': 3, 'red': 3, 'green': 1, 'yellow': 1})
2. defaultdict
:默认字典
defaultdict
是一个字典的子类,当访问不存在的键时,它会自动创建一个默认值。
from collections import defaultdict
# 使用 list 作为默认值工厂
word_positions = defaultdict(list)
text = "the quick brown fox jumps over the lazy dog".split()
for index, word in enumerate(text):
word_positions[word].append(index)
print(word_positions)
# 输出: defaultdict(<class 'list'>, {'the': [0, 6], 'quick': [1], 'brown': [2], 'fox': [3], 'jumps': [4], 'over': [5], 'lazy': [7], 'dog': [8]})
# 使用 int 作为默认值工厂
letter_counts = defaultdict(int)
for letter in "hello world":
letter_counts[letter] += 1
print(letter_counts)
# 输出: defaultdict(<class 'int'>, {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
3. deque
:双端队列
deque
(double-ended queue) 是一个线程安全、高性能的双端队列,可以在队列的两端添加或删除元素。
from collections import deque
queue = deque([1, 2, 3])
queue.append(4) # 在右端添加元素
queue.appendleft(0) # 在左端添加元素
print(queue) # 输出: deque([0, 1, 2, 3, 4])
queue.pop() # 从右端删除元素
queue.popleft() # 从左端删除元素
print(queue) # 输出: deque([1, 2, 3])
queue.rotate(1) # 向右旋转 1 个位置
print(queue) # deque([3, 1, 2])
4. namedtuple
:命名元组
namedtuple
是一个创建具有命名属性的元组的工厂函数。 它可以提高代码的可读性和可维护性。
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(x=10, y=20)
print(p.x) # 输出: 10
print(p.y) # 输出: 20
print(p) # 输出: Point(x=10, y=20)
# _fields 属性:访问所有字段名
print(p._fields) # ('x', 'y')
# _asdict() 方法:将 namedtuple 转换为 OrderedDict
print(p._asdict()) # OrderedDict([('x', 10), ('y', 20)])
# _replace() 方法:创建一个新的 namedtuple,替换指定字段的值
p2 = p._replace(x=30)
print(p2) # Point(x=30, y=20)
5. OrderedDict
:有序字典
OrderedDict
是一个字典的子类,它会记住键的插入顺序。 在 Python 3.7 之后,标准的 dict
也保留插入顺序,但在某些情况下,OrderedDict
仍然有用,例如,需要与旧版本的 Python 兼容,或者需要依赖其特定的行为(如 popitem
的默认行为)。
from collections import OrderedDict
ordered_dict = OrderedDict()
ordered_dict['a'] = 1
ordered_dict['b'] = 2
ordered_dict['c'] = 3
for key, value in ordered_dict.items():
print(key, value)
# 输出:
# a 1
# b 2
# c 3
# popitem(last=True) 移除并返回 (key, value) 键值对。 如果 last 为 true,则按 LIFO 后进先出的顺序返回键值对,否则按 FIFO 先进先出的顺序返回。
print(ordered_dict.popitem(last=False)) # ('a', 1)
6. ChainMap
:链式映射
ChainMap
将多个字典或映射组合成一个逻辑上的映射。 它不复制底层字典,而是创建一个指向它们的链。 当访问一个键时,ChainMap
会按照添加的顺序搜索每个字典,直到找到该键。
from collections import ChainMap
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
chain_map = ChainMap(dict1, dict2)
print(chain_map['a']) # 输出: 1
print(chain_map['b']) # 输出: 2 (从 dict1 中找到)
print(chain_map['c']) # 输出: 4
# parents 属性:返回一个新的 ChainMap,包含除了第一个字典之外的所有字典
print(chain_map.parents) # ChainMap({'b': 3, 'c': 4})
# new_child() 方法:创建一个新的 ChainMap,将一个新的字典添加到链的开头
new_map = chain_map.new_child({'d': 5})
print(new_map) # ChainMap({'d': 5}, {'a': 1, 'b': 2}, {'b': 3, 'c': 4})
四、 总结
itertools
提供了强大的迭代器工具,可以高效地处理数据序列;functools
提供了高阶函数工具,可以简化函数操作和提高代码可重用性; collections
提供了特殊的数据结构,可以解决内置数据结构的局限性。 熟练掌握这些模块,能够编写出更简洁、高效、可读性更强的 Python 代码。
五、 更有效地使用标准库
利用itertools
生成无限序列,组合序列和排列,functools
可以帮助我们简化函数调用和缓存,而collections
提供了一些高级数据结构,这些数据结构在特定场景下非常有用。