Python-Basic Day-5 函数-生成器&装饰器

一、生成器

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']

带参数的装饰器,其核心特征是它包含三层函数。这就像一个俄罗斯套娃:

  1. 最外层函数(套娃外壳)repeat(num_times)

    • 作用 :接收你传给装饰器的参数(num_times=3)。
    • 返回值 :它不返回最终结果,而是返回第二层函数,也就是一个真正的装饰器。
  2. 中间层函数(真正的装饰器)decorator(func)

    • 作用 :接收被装饰的函数(greet)。
    • 返回值 :返回第三层函数 ,也就是包装器 wrapper
  3. 最内层函数(包装器)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 函数执行结果
相关推荐
W如Q扬20 小时前
python程序使用supervisor启停
python·supervisor
Piar1231sdafa20 小时前
蓝莓果实检测与识别——基于decoupled-solo_r50_fpn_1x_coco模型实现
python
拾零吖20 小时前
Python 常用内置函数
python
行走的bug...21 小时前
python项目管理
开发语言·python
其美杰布-富贵-李21 小时前
tsai 完整训练流程实践指南
python·深度学习·时序学习·fastai
m0_4626052221 小时前
第N9周:seq2seq翻译实战-Pytorch复现-小白版
人工智能·pytorch·python
纪伊路上盛名在21 小时前
记1次BioPython Entrez模块Elink的debug
前端·数据库·python·debug·工具开发
CryptoRzz21 小时前
日本股票 API 对接实战指南(实时行情与 IPO 专题)
java·开发语言·python·区块链·maven
ss27321 小时前
考研加油上岸祝福弹窗程序
python
乾元21 小时前
基于时序数据的异常预测——短期容量与拥塞的提前感知
运维·开发语言·网络·人工智能·python·自动化·运维开发