前面的文章我们学完了Python的基础知识:数据类型、运算、三大结构、函数、文件操作、异常处理、模块包、面向对象。
你已经能写出完整的程序了。
但你可能看到过这样的代码:
今天就来搞定这三个进阶概念:迭代器、生成器、装饰器。
掌握它们,你的代码会更高效、更优雅、更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
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)
四、三者对比总结
五、常见错误与避坑指南
🔥 坑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进阶的三大核心概念:
掌握它们,你的Python代码会:
更高效(生成器省内存)
更优雅(装饰器复用代码)
更Pythonic(符合Python的设计哲学)
📌 如果觉得有用,点赞+在看+转发 给正在学Python的小伙伴!
进阶之路,一步一个脚印。我们下期见! 👋