Python的内置函数与库:深入理解`itertools`、`functools`和`collections`等标准库的高级用法。

Python 标准库高级用法:itertools, functools, collections 精讲

大家好,今天我们深入探讨 Python 标准库中三个非常强大的模块:itertoolsfunctoolscollections。 它们提供了许多高级的工具和数据结构,可以极大地简化代码,提高效率,并实现一些原本比较复杂的功能。

一、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 次。 如果 timesNone,则无限重复。

    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 的排列。 如果 rNone,则使用可迭代对象的长度。

    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提供了一些高级数据结构,这些数据结构在特定场景下非常有用。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注