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 函数执行结果
相关推荐
Yue丶越41 分钟前
【Python】基础语法入门(四)
linux·开发语言·python
AI街潜水的八角4 小时前
Python电脑屏幕&摄像头录制软件(提供源代码)
开发语言·python
hadage2334 小时前
--- git 的一些使用 ---
开发语言·git·python
笨笨聊运维10 小时前
CentOS官方不维护版本,配置python升级方法,无损版
linux·python·centos
Gerardisite10 小时前
如何在微信个人号开发中有效管理API接口?
java·开发语言·python·微信·php
小毛驴85011 小时前
软件设计模式-装饰器模式
python·设计模式·装饰器模式