在 Python 中,迭代器(Iterator)、生成器(Generator)和装饰器(Decorator)是三个非常强大且常用的核心概念,被称为“Python 三大神器”。它们能让你写出更加高效、优雅和易读的 Pythonic 代码。本文将深入浅出地讲解这三者的概念、原理和用法。


1. 迭代器 (Iterator)

什么是迭代器?

迭代器是一个可以记住遍历位置的对象。它提供了一种惰性计算的机制,即需要时才生成下一个元素,而不是一次性将所有数据加载到内存中。这使得它可以处理巨大的数据集合而不会消耗大量内存。

迭代器对象必须实现两个特殊方法,它们合称为 迭代器协议 (Iterator Protocol)

  • __iter__(): 返回迭代器对象本身(通常是 self)。

  • __next__(): 返回容器的下一个元素。如果没有更多元素,则抛出 StopIteration 异常。

如何创建和使用迭代器?

任何实现了 __iter__() 和 __next__() 方法的对象都是一个迭代器。

代码示例 1:自定义迭代器

python

class MyCounter:
    """一个简单的自定义迭代器,从 1 数到 n"""
    def __init__(self, n):
        self.n = n
        self.current = 1

    def __iter__(self):
        # 返回迭代器对象自身
        return self

    def __next__(self):
        # 返回下一个值
        if self.current <= self.n:
            value = self.current
            self.current += 1
            return value
        else:
            # 没有更多元素时,抛出 StopIteration 异常
            raise StopIteration

# 使用自定义迭代器
counter = MyCounter(3)
for num in counter:
    print(num)
# 输出:
# 1
# 2
# 3

# 你也可以手动调用 next()
counter2 = MyCounter(2)
print(next(counter2)) # 输出: 1
print(next(counter2)) # 输出: 2
# print(next(counter2)) # 再次调用会抛出 StopIteration

代码示例 2:使用内置迭代器
我们常用的 for...in 循环背后其实就是迭代器在工作。列表、元组、字典、字符串等可迭代对象 (Iterable) 都不是迭代器本身,但可以通过 iter() 函数获取它们的迭代器。

python

my_list = [1, 2, 3]
# my_list 是可迭代对象 (Iterable),但不是迭代器 (Iterator)
# iter(my_list) 会返回一个列表迭代器对象
list_iterator = iter(my_list)

print(next(list_iterator)) # 输出: 1
print(next(list_iterator)) # 输出: 2
print(next(list_iterator)) # 输出: 3
# print(next(list_iterator)) # 抛出 StopIteration

# for 循环的本质:
# for element in iterable:
#     print(element)
# 等价于:
# iterator = iter(iterable)
# while True:
#     try:
#         element = next(iterator)
#         print(element)
#     except StopIteration:
#         break

2. 生成器 (Generator)

什么是生成器?

生成器是一种特殊的迭代器,它使得编写迭代器变得更加简单和优雅。你不需要再像上面那样定义一个类并实现 __iter__() 和 __next__() 方法,而只需要使用一个包含 yield 关键字的函数。

生成器的核心是 yield 关键字。当函数执行到 yield 时,它会“暂停”并将值返回给调用者,同时保留当前所有的状态(局部变量、指令指针等)。下次调用 next() 时,函数会从上次 yield 的位置继续执行。

如何创建生成器?

创建生成器有两种主要方法:

  1. 生成器函数 (Generator Function):使用 def 定义,但使用 yield 而不是 return 返回值。

  2. 生成器表达式 (Generator Expression):类似于列表推导式,但使用圆括号 ()

代码示例 3:生成器函数

python

def my_counter(n):
    """一个简单的生成器函数,功能与 MyCounter 类相同"""
    current = 1
    while current <= n:
        yield current  # 在此处暂停,返回 current 的值
        current += 1
    # 函数结束时,会自动引发 StopIteration

# 使用生成器函数
# 调用生成器函数并不会立即执行,而是返回一个生成器对象
gen = my_counter(3)
print(gen)  # 输出: <generator object my_counter at 0x...>

# 生成器是迭代器,可以使用 next() 或 for 循环
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3
# print(next(gen)) # 抛出 StopIteration

# 更常见的用法是直接用 for 循环
for num in my_counter(3):
    print(num)

代码示例 4:生成器表达式 & 内存优势

python

# 列表推导式:一次性生成所有数据,占用内存
list_comp = [x * x for x in range(1000000)] # 内存中有一个包含100万个元素的列表

# 生成器表达式:惰性计算,几乎不占内存
gen_exp = (x * x for x in range(1000000)) # 几乎不占内存,只在需要时计算下一个平方数

print(next(gen_exp)) # 输出: 0
print(next(gen_exp)) # 输出: 1
print(next(gen_exp)) # 输出: 4

# 生成器在处理大规模数据(如大文件)时优势巨大
def read_large_file(file_path):
    """生成器函数,逐行读取大文件,避免内存溢出"""
    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            yield line.strip()

# 使用方式
# for line in read_large_file('huge_log_file.txt'):
#     process(line) # 一次只处理一行,内存友好

3. 装饰器 (Decorator)

什么是装饰器?

装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数(或可调用对象)。装饰器的作用是在不修改原函数代码的情况下,为函数增加新的功能,遵循“开放-封闭”原则。

装饰器通常用于添加日志、性能测试、事务处理、权限校验、缓存等通用功能。

如何创建和使用装饰器?

代码示例 5:简单的装饰器

python

def my_decorator(func):
    """一个简单的装饰器,在函数执行前后打印信息"""
    def wrapper():
        print("Something is happening before the function is called.")
        func()  # 执行被装饰的原始函数
        print("Something is happening after the function is called.")
    return wrapper # 返回内部包装好的函数

# 使用装饰器的语法糖 @
@my_decorator
def say_hello():
    print("Hello!")

# 调用被装饰后的函数
say_hello()
# 输出:
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.

# 不使用 @ 语法糖的等价写法:
# def say_hello():
#     print("Hello!")
# say_hello = my_decorator(say_hello) # 手动包装
# say_hello()

代码示例 6:处理带参数的函数

python

def decorator_with_args(func):
    """一个能处理被装饰函数参数的装饰器"""
    def wrapper(*args, **kwargs): # 使用 *args 和 **kwargs 接收任意参数
        print(f"Decorator: About to call {func.__name__}")
        result = func(*args, **kwargs) # 将参数原封不动传给原函数
        print(f"Decorator: Finished calling {func.__name__}")
        return result # 返回原函数的执行结果
    return wrapper

@decorator_with_args
def greet(name):
    return f"Hello, {name}!"

@decorator_with_args
def add(a, b):
    return a + b

print(greet("Alice"))
# 输出:
# Decorator: About to call greet
# Decorator: Finished calling greet
# Hello, Alice!

print(add(5, 3))
# 输出:
# Decorator: About to call add
# Decorator: Finished calling add
# 8

代码示例 7:带参数的装饰器

python

def repeat(num_times):
    """装饰器工厂:接受参数,返回一个真正的装饰器"""
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result # 通常返回最后一次调用的结果
        return wrapper
    return decorator_repeat

@repeat(num_times=4)
def say_hi():
    print("Hi!")

say_hi()
# 输出:
# Hi!
# Hi!
# Hi!
# Hi!

代码示例 8:内置装饰器 @functools.wraps
装饰器会掩盖原函数的元信息(如名字、文档字符串)。使用 functools.wraps 可以保留这些信息,这是一个最佳实践。

python

import functools

def smart_decorator(func):
    @functools.wraps(func) # 保留 func 的元信息
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@smart_decorator
def example():
    """这是一个示例函数的文档字符串。"""
    pass

print(example.__name__) # 输出: 'example' (没有 wraps 会输出 'wrapper')
print(example.__doc__)  # 输出: '这是一个示例函数的文档字符串。'

总结与对比

特性 迭代器 (Iterator) 生成器 (Generator) 装饰器 (Decorator)
本质 实现了 __iter__ 和 __next__ 的对象 特殊的迭代器,使用 yield 高阶函数,接受函数并返回函数
目的 提供一种遍历集合元素的通用方式 简化迭代器创建惰性求值节省内存 动态增强函数功能代码复用
关键语法 __iter__()__next__()StopIteration yield(x for x in ...) @decorator_name, 闭包
内存效率 高(惰性) 非常高(按需生成值) 不直接影响
主要优势 定义明确的遍历协议 语法简洁内存友好处理无限序列 不侵入原有代码解耦功能

关系与协作

  • 生成器是迭代器的“语法糖”,让你更方便地创建迭代器。

  • 装饰器可以装饰任何可调用对象,包括普通函数、类方法,甚至生成器函数

  • 你可以编写一个装饰器来增强生成器函数的行为(例如,自动处理 StopIteration 或记录生成的值)。

掌握这“三大神器”是迈向高级 Python 程序员的必经之路,它们能极大地提升你代码的效率、可读性和可维护性。

更多推荐