【30天玩转python】装饰器与闭包
装饰器与闭包
装饰器和闭包是 Python 中非常强大的特性。理解它们不仅有助于写出更简洁和模块化的代码,还能极大地提高代码的复用性和灵活性。本节将详细介绍装饰器与闭包的概念、用法及其在实际编程中的应用。
1. 闭包
闭包(Closure)是指一个函数在其作用域之外调用时,依然能够访问其作用域内的变量。闭包函数可以捕获并保存外部函数中的局部变量,即使外部函数已经返回,这些变量依然能够被访问。
1.1 闭包的定义
要形成闭包,需要满足以下条件:
- 必须有一个嵌套函数(即函数内部定义了另一个函数)。
- 嵌套函数必须引用外部函数中的变量。
- 外部函数的返回值是嵌套函数。
1.2 示例
def outer_func(msg):def inner_func():print(msg) # 引用了外部函数的变量return inner_funcclosure = outer_func("Hello, Closure!")
closure() # 输出:Hello, Closure!
在上面的例子中,inner_func()
是一个闭包,因为它访问了 outer_func()
的局部变量 msg
,即使 outer_func()
已经返回。
1.3 闭包的应用
闭包可以用于保存状态或对某些参数进行预先配置。它经常用于工厂模式或装饰器中。
计数器闭包:
def make_counter():count = 0def counter():nonlocal count # 声明count是外部函数的变量count += 1return countreturn countercounter_a = make_counter()
print(counter_a()) # 输出:1
print(counter_a()) # 输出:2counter_b = make_counter()
print(counter_b()) # 输出:1
2. 装饰器
装饰器(Decorator)是一个可以改变其他函数行为的函数。它允许在不修改原函数代码的情况下,向原函数添加新的功能。这使得代码更加灵活和可复用。
2.1 装饰器的定义
装饰器本质上是一个接受函数作为参数的函数,通常返回一个包装函数。这个包装函数会在原函数执行前或执行后添加额外的逻辑。
def decorator_func(func):def wrapper():print("在执行函数之前")func() # 调用原函数print("在执行函数之后")return wrapper@decorator_func # 使用装饰器
def say_hello():print("Hello!")say_hello()
# 输出:
# 在执行函数之前
# Hello!
# 在执行函数之后
在这个例子中,@decorator_func
是装饰器语法糖,它等价于 say_hello = decorator_func(say_hello)
。
2.2 带参数的装饰器
装饰器可以处理带有参数的函数。为了做到这一点,我们需要在包装函数中接受参数并传递给原函数。
def decorator_func(func):def wrapper(*args, **kwargs): # 接受任意参数print("在执行函数之前")result = func(*args, **kwargs)print("在执行函数之后")return resultreturn wrapper@decorator_func
def add(a, b):return a + bprint(add(2, 3)) # 输出:在执行函数之前 5 在执行函数之后
2.3 带参数的装饰器(装饰器工厂)
如果需要让装饰器自身接受参数,我们需要使用一个多层嵌套的函数,即装饰器工厂。
def decorator_with_args(msg):def decorator_func(func):def wrapper(*args, **kwargs):print(f"Message: {msg}")return func(*args, **kwargs)return wrapperreturn decorator_func@decorator_with_args("装饰器参数")
def greet(name):print(f"Hello, {name}!")greet("Alice")
# 输出:
# Message: 装饰器参数
# Hello, Alice!
3. 装饰器的实际应用
装饰器在实际编程中非常有用,尤其是在需要为多个函数添加相同的功能时。以下是一些常见的应用场景。
3.1 记录函数执行时间
通过装饰器来记录函数的执行时间,这在性能调优时非常有用。
import timedef timer(func):def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"{func.__name__} 执行时间: {end_time - start_time:.4f} 秒")return resultreturn wrapper@timer
def slow_function():time.sleep(2)slow_function()
# 输出:slow_function 执行时间: 2.0001 秒
3.2 权限校验
装饰器可以用于实现权限校验,比如在某些 API 接口中,只允许授权用户访问。
def requires_permission(func):def wrapper(user):if user.get('is_admin'):return func(user)else:print("无权限访问")return wrapper@requires_permission
def access_admin_panel(user):print("访问管理后台")user = {"username": "alice", "is_admin": True}
access_admin_panel(user) # 输出:访问管理后台user = {"username": "bob", "is_admin": False}
access_admin_panel(user) # 输出:无权限访问
3.3 缓存(Memoization)
装饰器可以用于缓存函数的计算结果,从而避免重复计算。这在处理递归函数时尤其有用,比如计算斐波那契数列。
def memoize(func):cache = {}def wrapper(n):if n not in cache:cache[n] = func(n)return cache[n]return wrapper@memoize
def fibonacci(n):if n <= 1:return nreturn fibonacci(n-1) + fibonacci(n-2)print(fibonacci(10)) # 输出:55
4. 小结
- 闭包:允许嵌套函数记住并访问外部函数的变量,哪怕外部函数已经返回。常用于保持状态或函数工厂。
- 装饰器:是一种能够在不改变函数本身的情况下扩展其功能的工具,非常适合代码复用和功能增强。
装饰器与闭包都是 Python 中的高级功能,掌握它们不仅能使代码更具灵活性,还能减少重复代码,提高代码的可读性和维护性。