2.17_装饰器

82 阅读4分钟

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_decoratorsay_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 使用classmethodstaticmethod装饰器

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

这些代码示例提供了装饰器的概念、定义和使用,以及类装饰器和方法装饰器的详细说明。您可以在本地环境中执行这些代码来验证输出。

4-1-2.png