一、生成器
1.1 生成器基础
生成器是一种特殊的迭代器,可以按需生成值,而不是一次性生成所有值。
python
# 普通函数 vs 生成器函数
def normal_function():
result = []
for i in range(5):
result.append(i)
return result
def generator_function():
for i in range(5):
yield i
# 使用对比
print("普通函数:", normal_function()) # 输出: [0, 1, 2, 3, 4]
gen = generator_function()
# 逐个获取生成器的值
print("生成器值:")
for value in gen:
print(value, end=" ") # 输出: 0 1 2 3 4
1.2 生成器用法
1.2.1 无限序列
python
def infinite_sequence():
"""生成无限序列"""
num = 0
while True:
yield num
num += 1
inf_gen = infinite_sequence()
# 此处为无限序列,需使用next()
for i in range(10):
print(next(inf_gen), end=" ")
1.2.2 生成器管道
生成器管道是一种高效处理数据序列的编程模式,其核心思想是惰性求值 和数据流 。它将一系列数据处理操作(如过滤、转换)串联起来,形成一个"流水线"。与一次性处理所有数据并生成庞大中间列表的传统方法不同,生成器管道中的每个环节都是一个生成器,它只在被请求时才处理一个数据项,并立即将其传递给下一个环节。这种机制带来了两大核心优势:极低的内存占用 (因为任何时候内存中都只存在单个正在处理的数据项)和即时的响应能力(无需等待所有数据处理完就能获得第一个结果)。它特别适合处理大规模文件、实时数据流或任何无法一次性装入内存的数据集,将复杂的批处理任务转变为一个轻量、高效的连续数据流。
python
def number_generator(n):
for i in range(n):
yield i
def square_number(numbers):
for number in numbers:
yield number ** 2
def even_filter(numbers):
for number in numbers:
if number % 2 == 0
yield number
numbers = number_generator(10)
squares = square_number(numbers)
even_number = even_filter(squares)
res = list(even_numbers)
print(res) # 输出: [0, 4, 16, 36, 64]
二、装饰器
2.1 装饰器基础
python
def simple_decorator(func):
"""简单装饰器"""
def wrapper():
print("函数执行前")
result = func()
print("函数执行后")
return result
return wrapper
@simple_decorator
def say_hello():
print("Hello, World!")
say_hello()
# 输出:
# 函数执行前
# Hello, World!
# 函数执行后
一个更实用的例子
python
import time
def timer_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.perf_counter() # 记录开始时间
result = func(*args, **kwargs) # 执行函数
end_time = time.perf_counter() # 记录结束时间
print(f"函数 {func.__name__} 耗时: {end_time - start_time:.4f} 秒")
return result
return wrapper
@timer_decorator
def fast_function():
time.sleep(0.1)
@timer_decorator
def slow_function():
time.sleep(1)
print("开始运行 fast_function...")
fast_function()
print("\n开始运行 slow_function...")
slow_function()
2.2 保留元数据的装饰器
2.2.1 问题根源
首先,我们回顾一下装饰器的本质:@decorator 等价于 my_function = decorator(my_function)。 这意味着,my_function 这个名字最终指向的不再是原来的函数,而是 decorator 内部返回的那个 wrapper 函数。这就带来了一个问题:我们丢失了原函数的"身份信息" 。
让我们先看看没有 @wraps 会发生什么:
python
# 假设我们有一个没有使用 @wraps 的装饰器
def simple_decorator(func):
def wrapper(*args, **kwargs):
"""这是wrapper的文档"""
print("执行一些操作...")
return func(*args, **kwargs)
return wrapper
@simple_decorator
def greet(name):
"""向某人问好"""
return f"Hello, {name}!"
print(f"函数名: {greet.__name__}")
print(f"函数文档: {greet.__doc__}")
输出会是:
makefile
函数名: wrapper
函数文档: 这是wrapper的文档
看到了吗?greet 函数的名字和文档字符串,都被 wrapper 函数的覆盖了。这对于调试、文档生成和内省(Introspection,即程序在运行时检查自身对象的能力)来说是灾难性的。我们希望 greet 看起来还像 greet。
2.2.2 解决方案
functools.wraps 就是专门用来解决这个问题的工具。它本身也是一个装饰器,用在 wrapper 函数上。 它的作用是:将原函数(func)的元数据(如 __name__, __doc__, __module__ 等)复制到 wrapper 函数上 ,从而伪装 wrapper,让它看起来和原函数一模一样。
python
from funtools import wraps
def simple_decorator(func):
@wraps(func) # <-- 关键就在这里!
def wrapper(*args, **kwargs):
"""这是wrapper的文档"""
print("执行一些操作...")
return func(*args, **kwargs)
return wrapper
@simple_decorator
def greet(name):
"""向某人问好"""
return f"Hello, {name}!"
print(f"函数名: {greet.__name__}")
print(f"函数文档: {greet.__doc__}")
# 输出
# 函数名: greet
# 函数文档: 向某人问好
2.3 装饰器的进阶用法
2.3.1 带参数的装饰器
python
def repeat(num_times):
"""重复执行装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
results = []
for _ in range(num_times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
return f"Greeted {name}"
result = greet("Alice")
print("返回值:", result)
# 输出:
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
# 返回值: ['Greeted Alice', 'Greeted Alice', 'Greeted Alice']
带参数的装饰器,其核心特征是它包含三层函数。这就像一个俄罗斯套娃:
-
最外层函数(套娃外壳) :
repeat(num_times)- 作用 :接收你传给装饰器的参数(
num_times=3)。 - 返回值 :它不返回最终结果,而是返回第二层函数,也就是一个真正的装饰器。
- 作用 :接收你传给装饰器的参数(
-
中间层函数(真正的装饰器) :
decorator(func)- 作用 :接收被装饰的函数(
greet)。 - 返回值 :返回第三层函数 ,也就是包装器
wrapper。
- 作用 :接收被装饰的函数(
-
最内层函数(包装器) :
wrapper(*args, **kwargs)- 作用:执行真正的逻辑------重复调用原函数,并收集结果。
- 返回值:返回最终的计算结果(一个包含所有返回值的列表)。
记住这个流程:外层函数接收参数,返回一个装饰器;这个装饰器再接收函数,返回一个包装器。
2.3.2 类装饰器
python
class ClassDecorator:
"""类装饰器"""
def __init__(self, func):
self.func = func
self.call_count = 0
def __call__(self, *args, **kwargs):
self.call_count += 1
print(f"第 {self.call_count} 次调用 {self.func.__name__}")
return self.func(*args, **kwargs)
@ClassDecorator
def example_function():
return "函数执行结果"
print(example_function()) # 输出: 第 1 次调用 example_function \n 函数执行结果
print(example_function()) # 输出: 第 2 次调用 example_function \n 函数执行结果