程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Python 基础

  • Python 进阶

    • 装饰器与生成器
      • 1. 装饰器 (Decorators)
        • 1.1 核心思想:函数是“一等公民”
        • 1.2 装饰器的基本实现
        • 1.3 语法糖:@ 符号
        • 1.4 装饰带参数和返回值的函数
        • 1.5 保持元信息:functools.wraps 的重要性
        • 1.6 带参数的装饰器
      • 2. 生成器 (Generators)
        • 2.1 创建生成器:yield 关键字
        • 2.2 生成器表达式 (Generator Expressions)
        • 2.3 生成器的优势与应用场景
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip 包管理工具
  • Python
  • Python 进阶
scholar
2025-07-23
目录

装饰器与生成器

# 装饰器与生成器

在Python的进阶道路上,装饰器(Decorators)和生成器(Generators)是两个极其强大且富有表现力的特性。它们不仅能让你的代码变得更加简洁、优雅,还能在处理复杂任务和大数据时显著提升性能。掌握它们是区分Python初学者和有经验开发者的一个重要标志。


# 1. 装饰器 (Decorators)

装饰器本质上是一个接收函数作为参数,并返回一个新函数的高阶函数。 它允许我们在不修改原函数代码和调用方式的前提下,为该函数动态地添加新的功能(如日志记录、性能测试、事务处理、权限校验等)。

# 1.1 核心思想:函数是“一等公民”

理解装饰器的前提是回顾并深刻理解Python中函数作为“一等公民”的特性:

  • 可以被赋值给变量:my_func = some_function
  • 可以作为参数传递给其他函数:process(some_function)
  • 可以作为其他函数的返回值:return some_function

这使得通过一个“包装”函数来扩展另一个函数的功能成为可能。

# 1.2 装饰器的基本实现

我们从一个简单的例子开始,手动实现一个装饰器来理解其工作原理。

def my_decorator(func_to_decorate):
    """这是一个简单的装饰器函数"""
    def wrapper():
        """这是包装函数,它包含了新功能和对原函数的调用"""
        print("--- 函数开始执行,我是新添加的功能 ---")
        
        func_to_decorate()  # 调用原始函数
        
        print("--- 函数执行结束,我也是新添加的功能 ---")
    
    return wrapper # 返回包装后的新函数

def say_hello():
    """一个简单的问候函数"""
    print("你好,世界!")

# 手动进行装饰
# 1. 将原函数传递给装饰器
# 2. 装饰器返回一个包装后的新函数
# 3. 将新函数重新赋值给原函数名
say_hello = my_decorator(say_hello)

# 现在调用 say_hello(),执行的其实是 wrapper() 函数
say_hello()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

输出:

--- 函数开始执行,我是新添加的功能 ---
你好,世界!
--- 函数执行结束,我也是新添加的功能 ---
1
2
3

# 1.3 语法糖:@ 符号

Python 提供了 @ 语法糖,让装饰器的使用更加直观和优雅,它能自动完成上面手动装饰的步骤。

@my_decorator
def say_goodbye():
    print("再见,世界!")

say_goodbye()

# 上面的 @my_decorator 写法完全等价于:
# say_goodbye = my_decorator(say_goodbye)
1
2
3
4
5
6
7
8

# 1.4 装饰带参数和返回值的函数

如果原始函数有参数和返回值,包装函数(wrapper)也必须能够接收这些参数并返回其结果,否则会丢失信息。我们使用 *args 和 **kwargs 来接收任意参数。

def timing_decorator(func):
    import time
    
    def wrapper(*args, **kwargs):
        start_time = time.time()
        
        # 将参数传递给原函数,并接收返回值
        result = func(*args, **kwargs) 
        
        end_time = time.time()
        print(f"函数 {func.__name__!r} 执行耗时: {end_time - start_time:.4f} 秒")
        
        return result # 将原函数的执行结果返回
    
    return wrapper

@timing_decorator
def calculate_sum(n):
    """计算从1到n的和"""
    total = 0
    for i in range(1, n + 1):
        total += i
    return total

sum_result = calculate_sum(1000000)
print(f"计算结果: {sum_result}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 1.5 保持元信息:functools.wraps 的重要性

你会发现,上例中 calculate_sum 函数被装饰后,它的元信息(如函数名 __name__、文档字符串 __doc__)丢失了,变成了包装函数 wrapper 的信息。

print(calculate_sum.__name__) # 输出: 'wrapper'
print(calculate_sum.__doc__)  # 输出: None
1
2

为了解决这个问题,Python 的 functools 模块提供了 wraps 装饰器。只需将它应用到你的包装函数上即可。

import time
from functools import wraps

def timing_decorator_fixed(func):
    @wraps(func) # 关键步骤:将被装饰函数的元信息复制到 wrapper 函数上
    def wrapper(*args, **kwargs):
        # ... (逻辑同上)
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数 {func.__name__!r} 执行耗时: {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@timing_decorator_fixed
def calculate_power(base, exponent):
    """计算一个数的幂"""
    return base ** exponent

print(calculate_power.__name__) # 输出: 'calculate_power'
print(calculate_power.__doc__)  # 输出: '计算一个数的幂'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

最佳实践:始终在你自定义的装饰器内部,使用 @wraps(func) 来装饰你的包装函数。

# 1.6 带参数的装饰器

如果想让装饰器本身接收参数(例如 @repeat(num_times=3)),需要再增加一层函数嵌套。

def repeat(num_times):            # 1. 最外层接收装饰器参数
    def decorator(func):          # 2. 第二层接收被装饰函数
        @wraps(func)
        def wrapper(*args, **kwargs): # 3. 最内层是真正的包装逻辑
            last_result = None
            for _ in range(num_times):
                last_result = func(*args, **kwargs)
            return last_result
        return wrapper
    return decorator

@repeat(num_times=3)
def greet(name):
    print(f"你好, {name}!")

greet("张三")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2. 生成器 (Generators)

生成器是 Python 中一种特殊的迭代器,它允许你“惰性地”生成一个值序列。与一次性计算并返回整个列表的普通函数不同,生成器一次只产生一个值,并在两次产生之间“暂停”其状态,极大地节省了内存。

# 2.1 创建生成器:yield 关键字

只要函数中包含 yield 关键字,它就不再是普通函数,而是一个生成器函数。调用生成器函数会返回一个生成器对象。

def simple_generator():
    print("生成器启动,即将产出第一个值")
    yield 1
    print("继续执行,即将产出第二个值")
    yield 2
    print("再次继续,即将产出第三个值")
    yield 3
    print("生成器执行结束")

# 调用生成器函数,得到一个生成器对象
gen = simple_generator()

# 生成器是迭代器,可以通过 next() 函数获取下一个值
print(f"获取值: {next(gen)}")
print(f"获取值: {next(gen)}")

# 也可以像迭代器一样在 for 循环中使用
for value in gen:
    print(f"循环中获取值: {value}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

当生成器函数执行到 yield 时,它会“暂停”并将 yield 后面的值返回给调用者。当 next() 再次被调用时,它会从上次暂停的地方继续执行,直到遇到下一个 yield 或函数执行完毕(此时会自动引发 StopIteration 异常)。

# 2.2 生成器表达式 (Generator Expressions)

类似于列表推导式,Python 提供了生成器表达式,用于快速创建简单的生成器。语法上,只需将列表推导式的 [] 替换为 ()。

# 列表推导式:立即创建完整列表,占用内存
list_comp = [i * i for i in range(1000)] 
# print(list_comp)

# 生成器表达式:返回一个生成器对象,不立即计算,几乎不占内存
gen_expr = (i * i for i in range(1000)) 
print(gen_expr) # 输出: <generator object <genexpr> at 0x...>

# 只有在遍历生成器时,值才会被逐个计算和产出
# for square in gen_expr:
#     print(square, end=' ')
1
2
3
4
5
6
7
8
9
10
11

# 2.3 生成器的优势与应用场景

生成器的核心优势在于内存效率和惰性求值。

  1. 处理大数据或无限序列 如果你需要处理一个巨大的文件,没必要一次性将它全部读入内存。

    def read_large_file(file_path):
        """逐行读取大文件,不占用大量内存"""
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                yield line.strip()
    
    # 即使文件有几GB大,内存占用也非常小
    # for log_entry in read_large_file("huge_log.txt"):
    #     if "ERROR" in log_entry:
    #         print(log_entry)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. 构建数据处理管道 可以将多个生成器连接起来,形成一个高效的数据处理流水线。数据在管道中是“流式”处理的,每个元素只在需要时才被计算。

    def get_numbers(n):
        for i in range(n):
            yield i
    
    def square_numbers(numbers_gen):
        for num in numbers_gen:
            yield num * num
    
    def filter_evens(squared_gen):
        for num in squared_gen:
            if num % 2 == 0:
                yield num
    
    # 将生成器连接起来,形成管道
    pipeline = filter_evens(square_numbers(get_numbers(10)))
    
    # 只有在迭代时,数据才会被逐个地、按需地流过整个管道
    for even_square in pipeline:
        print(even_square) # 输出 0, 4, 16, 36, 64
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  3. 协程(高级用法) 生成器的 send() 方法使其能够接收外部传入的值,这是构建协程(一种用户态的轻量级线程)的基础,在异步编程中扮演着关键角色。这部分内容更为高深,通常在学习异步IO(如 asyncio)时会深入探讨。

编辑此页 (opens new window)
上次更新: 2025/07/23, 06:33:16
面向对象综合案例
异常处理

← 面向对象综合案例 异常处理→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式