Python-迭代器、生成器与装饰器详解

1. 迭代器 (Iterator) - 可迭代的基石

  • 可迭代对象(Iterable):能够被迭代遍历的对象,即可以通过循环(如 Python 的 for 循环)逐个访问其内部元素的对象。
    • 本质:实现了 __iter__ (),调用 __iter__ ()应返回一个迭代器对象。
    • 生成迭代器:iter(可迭代对象)
  • 迭代器 (Iterator):执行遍历的工具,负责逐个返回元素(__next__())并保存遍历状态。
    • 本质:只要实现了 __iter__() 和 __next__() 这两个方法,任何对象都能叫迭代器。
    • 访问全部元素:不断调用__next__(),直至返回StopIteration异常(迭代器是一次性的,重新迭代需要创建新的迭代器
  • 让一个类成为迭代器:需要在类中实现两个方法__iter__()与 __next__()
    • __iter__():必须返回一个迭代器对象,大多数情况下,直接返回self即可(因为类本身就实现了__next__方法
    • __next__(self):每次迭代循环时被调用,用于返回下一个值。(没有更多元素可以返回时,必须抛出 StopIteration 异常
class Countdown:
    """一个从指定数字倒数到1的迭代器"""
    def __init__(self, start):
        # 初始化时,记录起始数字
        self.start = start
        # 当前值从起始数字开始
        self.current = start

    def __iter__(self):
        # 返回迭代器对象本身(self)
        # 在 for 循环开始时被调用
        print("  (调用 __iter__,返回迭代器对象)")
        return self

    def __next__(self):
        # 每次迭代(如 for 循环的下一轮)时被调用
        print(f"  (调用 __next__,当前值: {self.current})")
        
        # 判断是否还有值可以返回
        if self.current < 1:
            # 没有值了,必须抛出 StopIteration 异常
            print("  (抛出 StopIteration,迭代结束)")
            raise StopIteration
        
        # 保存当前值,用于返回
        value = self.current
        # 将当前值减1,为下一次调用做准备
        self.current -= 1
        # 返回本次迭代的值
        return value

# 使用示例:
print("1. 创建一个从5开始的倒计时器对象")
timer = Countdown(5)

print("\n2. 在 for 循环中使用它:")
#for循环过程:获取迭代器 -> 反复调用 __next__()
for num in timer: 
    print(f"   => 得到数字: {num}")

# 重要:迭代器是一次性的,如果再次尝试迭代,会立即结束
print("\n3. 尝试再次迭代(迭代器已耗尽):")
for num in timer:
    print(num)  # 这行不会执行,因为 __next__ 一开始就抛出 StopIteration
else:
    print("   (没有输出任何数字,因为迭代器已经为空)")

 2.生成器 (Generator) - 简洁的迭代器

生成器是一种更简洁的迭代器,创建生成器可以使用生成器函数(使用yield)或者生成器表达式(如(x**2 for x in range(10))

print(type(a)) #<class 'generator'>
print(isinstance(a,Iterator)) #True,生成器是一种更简洁的迭代器
  • 自动处理了__iter__()__next__()。
  • 自动处理状态保持和StopIteration异常,代码量大大减少。
  • 惰性求值、节省内存;适用于处理大型数据流或文件(调用1次__next__()只返回1个元素)。
  • 【生成器函数-for循环访问】代码示例:
def countdown_gen(start):
    while start >= 1:
        yield start #yield会暂停函数状态,返回对应值
        start -= 1

#for循环:先生成迭代器,再反复调用 __next__(),每次会从上次暂停处,继续往下执行
for num in countdown_gen(5):
 print(num)
  • 【生成器函数-手动访问】代码示例:
def countdown_gen(start):
    while start >= 1:
        yield start 
        start -= 1

a = countdown_gen(5) #调用生成器函数、返回一个迭代器对象
print(a.__next__()) #输出5
print(a.__next__()) #输出4
print(a.__next__())  #输出3
  • 【生成器表达式】代码示例:
# 1. 列表推导式:一次性创建所有数据
print("=== 列表推导式 ===")
list_comp = [x**2 for x in range(5)]  # 使用方括号 []
print(f"列表推导式结果: {list_comp}")   #[0, 1, 4, 9, 16]
print(f"类型: {type(list_comp)}") # <class 'list'>
print(f"占用内存大小(近似): {list_comp.__sizeof__()} 字节") #104 字节

# 2. 生成器表达式:创建一个生成器对象,不立即生成数据
print("\n=== 生成器表达式 ===")
gen_exp = (x**2 for x in range(5))  # 使用圆括号 ()
print(f"生成器表达式结果: {gen_exp}") #  <generator object <genexpr> at 0x0000018928981A40>
print(f"类型: {type(gen_exp)}") # <class 'generator'>
print(f"占用内存大小(近似): {gen_exp.__sizeof__()} 字节") # 64 字节

# 要获取生成器中的数据,需要遍历它
print("\n遍历生成器获取值:")
for value in gen_exp:
    print(value, end=" ")  # 输出: 0 1 4 9 16 

# 注意:生成器是一次性的,再次遍历将没有输出
print("\n\n再次遍历生成器(已耗尽):")
for value in gen_exp:
    print(value, end=" ")
else:
    print("(没有输出任何值)")

 

3. 装饰器 (Decorator) - 修改行为的优雅方式

  • 装饰器:一个修改函数或类的工具,本质上是一个高阶函数(接收函数作为参数,返回新函数)。
  • 高阶函数:能够接受函数作为参数能够返回函数的函数。
    • map(function, iterable):将指定的函数依次作用到可迭代对象(如列表)的每一个元素上,并返回一个迭代器
    • filter(function, iterable):根据指定的函数(判断函数)对可迭代对象的元素进行过滤,保留返回值为 True 的元素,并返回一个迭代器。
  • 闭包:一个函数内部再定义一个函数(内层函数可以访问外层函数的变量,但外层函数不能访问内层函数的变量。)
    • 作用:数据隐藏与封装
    • 关系:装饰器是闭包在修改函数行为时的一个经典应用。
# 1. 定义外层函数
def outer_function(message):
    # 2. 定义并返回内层函数(闭包)
    def inner_function():
        # 内层函数使用了外层函数的变量 message
        print(f"记忆中的消息是:{message}")

    #返回内层函数
    return inner_function

# 3. 创建两个不同的闭包实例
hello_closure = outer_function("你好!")
bye_closure = outer_function("再见!")

# 4. 调用闭包
hello_closure()  # 输出:记忆中的消息是:你好!
bye_closure()    # 输出:记忆中的消息是:再见!
  • 最简单的装饰器:打印出被装饰函数的执行耗时
import time

# 1. 定义装饰器(本质上是一个接收函数、返回新函数的高阶函数)
def timer(func):
    # 2. 定义包装函数(闭包),它接收任意参数
    def wrapper(*args, **kwargs):
        start_time = time.time()        # 执行前记录开始时间
        result = func(*args, **kwargs)  # 执行被装饰的原始函数
        end_time = time.time()          # 执行后记录结束时间
        print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.6f} 秒")
        return result                   # 返回原函数的执行结果
    return wrapper

# 3. 使用装饰器语法糖 @timer
@timer
def slow_function():
    time.sleep(1)  # 模拟一个耗时操作
    return "执行完毕"

# 4. 调用函数
print(slow_function())  
# 函数 slow_function 执行耗时: 1.006022 秒
# 执行完毕
  • 带参数的装饰器
# 1. 定义带参数的装饰器(装饰器工厂),times接收传入的参数
def repeat(times):
    """
    装饰器工厂:接收重复次数,返回真正的装饰器
    """
    print(f"进入装饰器工厂,要重复执行 {times} 次")

    # 2. 定义真正的装饰器,func接受传入的函数
    def decorator(func):
        print(f"  进入装饰器,正在装饰函数 {func.__name__}")

        # 3. 定义包装函数(闭包),被调用时才执行包装函数
        def wrapper(*args, **kwargs):
            last_result = None
            for i in range(times):
                print(f"    第 {i+1} 次执行 {func.__name__}")
                last_result = func(*args, **kwargs)
            return last_result
return wrapper return decorator # 4. 使用带参数的装饰器 print("--- 开始装饰函数 say_hello ---") @repeat(times=3) # 执行顺序:repeat(3) -> decorator(say_hello) -> wrapper def say_hello(name): """一个打招呼的函数""" print(f"Hello, {name}!") return "done" print("\n--- 开始调用函数 say_hello ---") result = say_hello("闭包") print(f"函数返回值: {result}") # 注意:此时查看函数名,会显示 wrapper,而不是 say_hello print(f"\n函数名: {say_hello.__name__}") # 输出:wrapper (因为被装饰器替换了)

PS C:\Users\wei.feng\Desktop\opt\server_test_platform> python .\debug.py
--- 开始装饰函数 say_hello ---
进入装饰器工厂,要重复执行 3 次
进入装饰器,正在装饰函数 say_hello

--- 开始调用函数 say_hello ---
第 1 次执行 say_hello
Hello, 闭包!
第 2 次执行 say_hello
Hello, 闭包!
第 3 次执行 say_hello
Hello, 闭包!
函数返回值: done

函数名: wrapper

1、say_hello()的__name__会被包装函数替换

2、包装函数为wrapper(),say_hello()的__name__为wrapper

3、保留被装饰函数的元信息:functools.wraps

  • 使用@functools.wraps(func)保留原函数的元信息(包括 __name____doc____module__ 等属性
import functools

# ---------- 1. 定义一个加 @wraps 的装饰器 ----------
def timer_with_wraps(func):
    @functools.wraps(func)  # 关键行
    def wrapper(*args, **kwargs):
        """这是 wrapper 的文档"""
        result = func(*args, **kwargs)
        return result
    return wrapper

@timer_with_wraps
def say_hello_with(name):
    """原函数:say_hello_with 的文档"""
    print(f"Hello, {name}!")

print("\n2. 加上 @wraps 的装饰器结果:")
print(f"   函数名: {say_hello_with.__name__}")  # 函数名: say_hello_with
print(f"   文档字符串: {say_hello_with.__doc__}") # 文档字符串: 原函数:say_hello_with 的文档
  • 应用场景:日志、计时、校验、缓存等
posted @ 2026-07-06 17:54  Fēngwèi  阅读(10)  评论(0)    收藏  举报