当前位置:首页>python>【从零开始学Python】篇12-进阶篇-三器(迭代、生成、装饰)

【从零开始学Python】篇12-进阶篇-三器(迭代、生成、装饰)

  • 2026-10-11 08:05:37
【从零开始学Python】篇12-进阶篇-三器(迭代、生成、装饰)

前面的文章我们学完了Python的基础知识:数据类型、运算、三大结构、函数、文件操作、异常处理、模块包、面向对象。

你已经能写出完整的程序了。

但你可能看到过这样的代码:

  • for i in range(10000000): 内存爆了怎么办?

  • 别人写的 @login_required 是什么意思?

  • yield 这个关键字从来没见过

今天就来搞定这三个进阶概念:迭代器、生成器、装饰器。

掌握它们,你的代码会更高效、更优雅、更Pythonic。


一、迭代器(Iterator)

1.1 什么是迭代?

迭代 = 一个一个地取出元素的过程。

# 这些都是迭代for i in [1, 2, 3]:    print(i)for c in "hello":    print(c)for k in {"name": "小明", "age": 18}:    print(k)

1.2 什么是可迭代对象(Iterable)?

可迭代对象:可以被 for 循环遍历的东西。

常见的有:列表、元组、字符串、字典、集合、文件对象等。

# 判断是否可迭代from collections.abc import Iterableprint(isinstance([1,2,3], Iterable))# Trueprint(isinstance("abc", Iterable))# Trueprint(isinstance(123, Iterable))# False

1.3 什么是迭代器(Iterator)?

迭代器:一个可以记住遍历位置的对象。它有两个特点:

  • 有 __iter__() 方法,返回自己

  • 有 __next__() 方法,返回下一个元素

# 从可迭代对象获取迭代器my_list =[1,2,3]iterator =iter(my_list)# 或 my_list.__iter__()# 手动获取元素print(next(iterator))# 1  或 iterator.__next__()print(next(iterator))# 2print(next(iterator))# 3print(next(iterator))# StopIteration 异常,没有更多元素了

1.4 为什么需要迭代器?

惰性求值:一次只产生一个元素,不一次性把所有数据都放进内存。

# 普通列表:一次性加载1000万个数字 → 内存爆炸 💥big_list =[i for i inrange(10000000)]# 迭代器:一次只产生一个 → 内存友好 ✅big_iterator =iter(range(10000000))next(big_iterator)# 只拿一个

1.5 自定义迭代器

class CountDown:    """倒计时迭代器"""    def __init__(self, start):        self.current = start    def __iter__(self):        return self  # 迭代器返回自己    def __next__(self):        if self.current < 0:            raise StopIteration        value = self.current        self.current -= 1        return value# 使用for num in CountDown(5):    print(num)  # 5 4 3 2 1 0   

二、生成器(Generator)

生成器是更简单的迭代器,用 yield 关键字代替 return。

2.1 生成器函数

def count_down(n):    while n >= 0:        yield n  # 暂停,返回 n        n -= 1# 调用生成器函数,得到一个生成器对象g = count_down(5)print(g)  # <generator object count_down at 0x...># 使用生成器for num in count_down(5):    print(num)  # 5 4 3 2 1 0# 或者手动获取g = count_down(3)print(next(g))  # 3print(next(g))  # 2print(next(g))  # 1print(next(g))  # 0print(next(g))  # StopIteration

2.2 yield vs return

returnyield
作用
结束函数,返回一个值
暂停函数,返回一个值
再次调用
重新执行
从暂停处继续
内存
一次性返回所有结果
惰性产生,逐个返回

2.3 生成器表达式

类似列表推导式,但用圆括号 () 而不是方括号 []。

# 列表推导式:立即生成所有数据squares_list = [x**2 for x in range(10)]print(squares_list)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]# 生成器表达式:惰性生成squares_gen = (x**2 for x in range(10))print(squares_gen)   # <generator object <genexpr> at 0x...># 逐个获取print(next(squares_gen))  # 0print(next(squares_gen))  # 1# 也可以 for 循环for val in (x**2 for x in range(5)):    print(val)  # 0 1 4 9 16

2.4 生成器 vs 列表:内存对比

import sys# 列表:占用大量内存list_nums = [i for i in range(1000000)]print(sys.getsizeof(list_nums))  # 约 8MB# 生成器:几乎不占内存gen_nums = (i for i in range(1000000))print(sys.getsizeof(gen_nums))   # 约 112 字节# 两者都可以遍历,但内存消耗天差地别

2.5 生成器的实际应用

场景1:读取大文件

def read_large_file(file_path):    """一行一行读取大文件,不一次性加载到内存"""    with open(file_path, 'r', encoding='utf-8') as f:        for line in f:            yield line.strip()# 使用for line in read_large_file("big_file.txt"):    process(line)  # 处理每一行

场景2:无限序列

def fibonacci():    """无限生成斐波那契数列"""    a, b = 0, 1    while True:        yield a        a, b = b, a + b# 取前10个fib = fibonacci()for i in range(10):    print(next(fib))  # 0 1 1 2 3 5 8 13 21 34# 不要直接 list(fibonacci()),会无限循环!

场景3:流水线处理

def read_logs(file_path):    for line in open(file_path):        yield line.strip()def filter_error(lines):    for line in lines:        if "ERROR" in line:            yield linedef extract_message(error_lines):    for line in error_lines:        yield line.split("ERROR:")[-1]# 流水线:一条龙处理,每步都是惰性logs = read_logs("app.log")errors = filter_error(logs)messages = extract_message(errors)for msg in messages:    print(msg)

三、装饰器(Decorator)

装饰器是在不修改函数代码的情况下,给函数增加额外功能。

3.1 函数是一等公民

在Python中,函数可以:

# 1. 赋值给变量def greet(name):    return f"Hello {name}"say_hello = greetprint(say_hello("小明"))  # Hello 小明# 2. 作为参数传递def run(func, value):    return func(value)print(run(greet, "小红"))  # Hello 小红# 3. 作为返回值def make_greeter(prefix):    def greeter(name):        return f"{prefix}{name}"    return greeterhello = make_greeter("Hi")print(hello("李四"))  # Hi 李四

3.2 最简单的装饰器

# 定义一个装饰器def my_decorator(func):    def wrapper():        print("函数执行前...")        func()        print("函数执行后...")    return wrapper# 使用装饰器@my_decoratordef say_hello():    print("Hello!")# 等价于# say_hello = my_decorator(say_hello)say_hello()# 输出:# 函数执行前...# Hello!# 函数执行后...

3.3 装饰带参数的函数

def timer(func):    """计算函数执行时间的装饰器"""    import time    def wrapper(*args, **kwargs):        start = time.time()        result = func(*args, **kwargs)        end = time.time()        print(f"{func.__name__} 耗时:{end-start:.4f}秒")        return result    return wrapper@timerdef slow_sum(n):    total = 0    for i in range(n):        total += i    return total@timerdef greet(name, greeting="你好"):    print(f"{greeting},{name}")# 使用slow_sum(10000000)   # slow_sum 耗时:0.23秒greet("小明")         # 你好,小明  greet 耗时:0.0000秒

3.4 带参数的装饰器

def repeat(times):    """重复执行函数的装饰器"""    def decorator(func):        def wrapper(*args, **kwargs):            results = []            for i in range(times):                result = func(*args, **kwargs)                results.append(result)            return results        return wrapper    return decorator@repeat(3)def say_hello():    return "Hello"print(say_hello())  # ['Hello', 'Hello', 'Hello']

3.5 多个装饰器叠加

def bold(func):    def wrapper():        return "<b>" + func() + "</b>"    return wrapperdef italic(func):    def wrapper():        return "<i>" + func() + "</i>"     return wrapper@bold@italicdef say():    return "Hello"print(say())  # <b><i>Hello</i></b># 从下往上执行:先 italic,再 bold

3.6 保留原函数信息:functools.wraps

from functools import wrapsdef my_decorator(func):    @wraps(func)  # 保留原函数的元信息    def wrapper(*args, **kwargs):        """wrapper 的文档"""        print("装饰器执行")        return func(*args, **kwargs)    return wrapper@my_decoratordef greet(name):    """greet 的文档"""    print(f"Hello {name}")print(greet.__name__)   # greet(没有@wraps会变成wrapper)print(greet.__doc__)    # greet 的文档

3.7 实战:常用装饰器

1. 登录验证装饰器

from functools import wrapsuser_logged_in = False  # 模拟登录状态def login_required(func):    @wraps(func)    def wrapper(*args, **kwargs):        if not user_logged_in:            return "请先登录"        return func(*args, **kwargs)    return wrapper@login_requireddef view_profile(user_id):    return f"查看用户 {user_id} 的档案"@login_requireddef delete_post(post_id):    return f"删除文章 {post_id}"print(view_profile(100))   # 请先登录

2. 重试装饰器

import timefrom functools import wrapsdef retry(max_retries=3, delay=1):    def decorator(func):        @wraps(func)        def wrapper(*args, **kwargs):            last_exception = None            for attempt in range(max_retries):                try:                    return func(*args, **kwargs)                except Exception as e:                    last_exception = e                    print(f"尝试 {attempt+1}/{max_retries} 失败: {e}")                    if attempt < max_retries - 1:                        time.sleep(delay)            raise last_exception        return wrapper    return decorator@retry(max_retries=3, delay=0.5)def unstable_function():    import random    if random.random() < 0.7:        raise ValueError("随机失败")    return "成功"print(unstable_function())

3. 缓存装饰器

from functools import wraps, lru_cache# 方式1:手动实现def cache(func):    _cache = {}    @wraps(func)    def wrapper(*args):        if args in _cache:            print("从缓存获取结果")            return _cache[args]        print("计算结果")        result = func(*args)        _cache[args] = result        return result    return wrapper@cachedef fib(n):    if n < 2:        return n    return fib(n-1) + fib(n-2)print(fib(10))  # 只计算一次,后面从缓存取# 方式2:使用内置的 lru_cache(更推荐)from functools import lru_cache@lru_cache(maxsize=128)def fib2(n):    if n < 2:        return n    return fib2(n-1) + fib2(n-2)print(fib2(100))  # 轻松计算,有缓存

4. 日志装饰器

import timefrom functools import wrapsdef log(func):    @wraps(func)    def wrapper(*args, **kwargs):        print(f"[调用] {func.__name__} 参数: {args}, {kwargs}")        start = time.time()        result = func(*args, **kwargs)        end = time.time()        print(f"[返回] {func.__name__} 结果: {result} 耗时: {end-start:.4f}s")        return result    return wrapper@logdef add(a, b):    return a + b@logdef multiply(a, b):    return a * badd(3, 5)multiply(4, 6)

四、三者对比总结

特性
迭代器
生成器
装饰器
核心作用
惰性遍历
惰性生成数据
增强函数功能
实现方式
__iter__
 + __next__
yield@
 + 嵌套函数
内存友好
✅
✅
不直接相关
能否复用
❌(遍历完就没)
❌(遍历完就没)
✅
难易程度
中等
简单
中等
使用场景
自定义遍历逻辑
大序列、无限序列
日志、缓存、权限

五、常见错误与避坑指南

🔥 坑1:生成器只能遍历一次

g = (x for x in range(5))print(list(g))  # [0,1,2,3,4]print(list(g))  # [] 空了!# 需要重新创建g2 = (x for x in range(5))

🔥 坑2:装饰器忘记 @wraps

def bad_decorator(func):    def wrapper():        return func()    return wrapper@bad_decoratordef hello():    """重要文档"""    passprint(hello.__name__)  # wrapper(丢失了原函数名)print(hello.__doc__)   # None(丢失了文档)# 加上 @wraps 解决

🔥 坑3:在类中装饰器用错

class MyClass:    @staticmethod    def method():  # 这个可以        pass    @timer  # ❌ 如果 timer 没有处理 self,会出错    def method2(self):        pass

🔥 坑4:生成器内存占用反而更多

# 生成器本身内存小,但每次 next 都要计算# 对于小数据,列表可能更快# 100个以内的数据,用列表即可

六、实战综合案例

案例:带缓存和日志的斐波那契

import timefrom functools import wraps, lru_cachedef log_and_time(func):    """日志+计时的装饰器"""    @wraps(func)    def wrapper(*args, **kwargs):        print(f"[调用] {func.__name__}({args})")        start = time.time()        result = func(*args, **kwargs)        end = time.time()        print(f"[返回] {result} 耗时: {(end-start)*1000:.2f}ms")        return result    return wrapper@log_and_time@lru_cache(maxsize=None)def fib(n):    if n < 2:        return n    return fib(n-1) + fib(n-2)print(fib(35))# 第一次:计算所有,打印日志# 第二次:fib(35) 直接返回缓存,极快print(fib(35))  # 从缓存取,不打印日志

写在最后

迭代器、生成器、装饰器是Python进阶的三大核心概念:

概念
一句话总结
迭代器
实现 __next__,让你可以 for 循环
生成器
用 yield 懒洋洋地产出数据,省内存
装饰器
在不改原函数的前提下,给它穿衣服加功能

掌握它们,你的Python代码会:

  • 更高效(生成器省内存)

  • 更优雅(装饰器复用代码)

  • 更Pythonic(符合Python的设计哲学)

📌 如果觉得有用,点赞+在看+转发 给正在学Python的小伙伴!

进阶之路,一步一个脚印。我们下期见! 👋

最新文章

随机文章