2.17 装饰器
2.17.1 装饰器的概念
2.17.1.1 装饰器的基本概念与作用
# 装饰器用于扩展函数或方法的功能
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
输出:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
解释: 装饰器my_decorator在say_hello函数执行前后添加了额外的行为。
2.17.1.2 装饰器的工作原理
# 装饰器实际上是在函数定义时,将原函数替换为装饰器返回的函数
def simple_decorator(func):
def wrapper():
print("Decorated!")
return func()
return wrapper
@simple_decorator
def func():
print("Function runs.")
func()
输出:
Decorated!
Function runs.
解释: func被替换为wrapper函数,wrapper函数内部调用了原始的func。
2.17.1.3 装饰器的应用场景
# 装饰器可以用来做日志记录、性能测试、事务处理、缓存等
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args {args}, kwargs {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_decorator
def add(a, b):
return a + b
add(2, 3)
输出:
Calling add with args (2, 3), kwargs {}
add returned 5
解释: 装饰器log_decorator记录了函数调用的参数和返回值。
2.17.1.4 装饰器与闭包的关系
# 装饰器本质上是一个闭包,它捕获了被装饰函数
def repeat_decorator(repeat_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(repeat_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat_decorator(3)
def greet(name):
print(f"Hello {name}!")
greet("World")
输出:
Hello World!
Hello World!
Hello World!
解释: repeat_decorator是一个返回装饰器的函数,它是一个闭包,捕获了repeat_times参数。
2.17.1.5 装饰器的高级用法与技巧
# 使用functools.wraps保持元数据
from functools import wraps
def debug.decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args {args}, kwargs {kwargs}")
return func(*args, **kwargs)
return wrapper
@debug.decorator
def compute(x, y):
return x + y
print(compute.__name__) # 应该输出compute而不是wrapper
输出:
Calling compute with args (1, 2), kwargs {}
compute
解释: @wraps(func)保持了原始函数的名字和文档字符串。
2.17.2 函数装饰器的定义与使用
2.17.2.1 定义一个简单的函数装饰器
def my_decorator(func):
def wrapper():
print("Before the function")
func()
print("After the function")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
输出:
Before the function
Hello!
After the function
2.17.2.2 带参数的函数装饰器
def repeat(n):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator_repeat
@repeat(3)
def greet():
print("Hello!")
greet()
输出:
Hello!
Hello!
Hello!
2.17.2.3 装饰器与函数的返回值
def add_logging(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@add_logging
def get_full_name(first_name, last_name):
return f"{first_name} {last_name}"
print(get_full_name("John", "Doe"))
输出:
John Doe
get_full_name returned John Doe
2.17.2.4 使用functools.wraps保持元数据
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
print("Hello!")
return "Hello"
print(say_hello())
输出:
Something is happening before the function is called.
Hello!
Hello
2.17.2.5 装饰器链式调用
def first_decorator(func):
def wrapper():
print("First decorator")
return func()
return wrapper
def second_decorator(func):
def wrapper():
print("Second decorator")
return func()
return wrapper
@first_decorator
@second_decorator
def say_hello():
print("Hello!")
say_hello()
输出:
Second decorator
First decorator
Hello!
2.17.2.6 函数装饰器的性能考虑
# 装饰器可能会引入额外的函数调用,影响性能
def simple_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@simple_decorator
def compute(x, y):
return x + y
# 性能测试
import timeit
setup_code = "from __main__ import compute"
stmt = "compute(2, 3)"
decorated_time = timeit.timeit(stmt, setup=setup_code, number=100000)
print(f"Decorated time: {decorated_time}")
输出: 取决于机器性能,装饰器可能会增加执行时间。
2.17.3 类装饰器
2.17.3.1 类装饰器的基本概念与应用
def class_decorator(cls):
class Wrapper:
def __init__(self, *args, **kwargs):
self._instance = cls(*args, **kwargs)
def __getattr__(self, name):
return getattr(self._instance, name)
return Wrapper
@class_decorator
class Greeter:
def __init__(self, name):
self.name = name
def greet(self):
print(f"Hello {self.name}!")
greeter = Greeter("World")
greeter.greet() # 这实际上创建了一个Wrapper实例
输出:
Hello World!
2.17.3.2 定义一个简单的类装饰器
def my_class_decorator(cls):
class DecoratedClass:
def __init__(self, *args, **kwargs):
self._instance = cls(*args, **kwargs)
def __getattr__(self, item):
return getattr(self._instance, item)
return DecoratedClass
@my_class_decorator
class Calculator:
def add(self, a, b):
return a + b
calculator = Calculator()
print(calculator.add(2, 3))
输出:
5
2.17.3.3 修改类的行为与属性
def add_init_param(cls):
class NewCls(cls):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.new_param = "new attribute"
return NewCls
@add_init_param
class MyClass:
pass
obj = MyClass()
print(obj.new_param)
输出:
new attribute
2.17.3.4 类装饰器与元类的关系
# 类装饰器本质上是一个工厂函数,返回一个新的类
def my_class_decorator(cls):
class NewCls(cls):
pass
return NewCls
@my_class_decorator
class MyClass:
pass
MyClass实际上是NewCls的一个实例
isinstance(MyClass, type) # True
#### 2.17.3.5 使用装饰器扩展类功能
```python
def add_method(cls):
class NewCls(cls):
def new_method(self):
print("New method called")
return NewCls
@add_method
class MyClass:
pass
obj = MyClass()
obj.new_method()
输出:
New method called
2.17.3.6 使用classmethod与staticmethod装饰器
def class_method_decorator(method):
print("Decorating class method")
return classmethod(method)
def static_method_decorator(method):
print("Decorating static method")
return staticmethod(method)
class MyClass:
@class_method_decorator
def class_method(cls):
print("Class method called")
@static_method_decorator
def static_method():
print("Static method called")
MyClass.class_method()
MyClass.static_method()
输出:
Decorating class method
Class method called
Decorating static method
Static method called
这些代码示例提供了装饰器的概念、定义和使用,以及类装饰器和方法装饰器的详细说明。您可以在本地环境中执行这些代码来验证输出。