Python装饰器decorator(装饰器装饰函数、类,及高级编程技巧的实现案例详解)

装饰器是Python中一个非常强大的功能。
强大在于:装饰函数(简单功能) 与 装饰类(包含多个高级功能)。

1、装饰器装饰函数:

它允许用户在不修改原有函数定义的情况下,增加函数的新功能。装饰器本质上是一个接收函数作为参数并返回一个新函数的函数。

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.

通过此案例可以看出,
**decorator装饰器的写法:**装饰器函数的参数为应为函数,装饰器函数中需要定义wrapper()函数。
**decorator装饰器的功能:**增加了被装饰函数的功能,在此案例中相当于say_hello()=my_decorator(say_hello)
**实现扩展被装饰函数功能的工具:**wrapper() 函数称为“包装器”或“包裹函数”主要作用是扩展或修改被装饰函数的行为。

值得注意的是:Wrapper函数可以传入参数。

1.2、装饰器函数通过包装器传入参数案例

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello {name}!")
    
say_hello(name="Alice")

输出为:

Something is happening before the function is called.
Hello Alice!
Something is happening after the function is called.

本例中的name="Alice"通过wrapper()函数传入。
有时我们需要传入变长参数,可以改为被装饰函数为:

@my_decorator
def say_hello(**kwargs):
    args_output = ', '.join([f"{key}={value}" for key, value in kwargs.items()])
    print(f"Hello with these details: {args_output}")

say_hello(name="Alice", age=25, location="Wonderland")

输出为:

Something is happening before the function is called.
Hello with these details: name=Alice, age=25, location=Wonderland
Something is happening after the function is called.

2、装饰器装饰类

通过装饰类,可以
1、修改或增强类的行为,比如添加额外的属性、修改方法、
2、注册类到某些注册表中
3、实例化过程中执行额外的逻辑。

2.1、类装饰器的基本结构

装饰函数接收类作为参数,返回新的类或者原始类的修改版本

def add_method(cls):
    def print_name(self):
        print(f"The class name is {self.__class__.__name__}")
    cls.print_name = print_name
    return cls

@add_method
class MyClass:
    pass

obj = MyClass()
obj.print_name()  # 输出: The class name is MyClass

装饰器 add_method 直接修改了传入的类 cls,给它添加了一个新的方法 print_name,然后返回修改后的类。

2.2类装饰器高级用途,自动注册

自动将类注册到某些注册表(字典)中,常用于插件系统或自动发现机制。
示例:注册类装饰器

registry = {}

def register_class(target_class):
    registry[target_class.__name__] = target_class
    return target_class

@register_class
class Widget:
    def __init__(self, name):
        self.name = name

@register_class
class Gadget:
    def __init__(self, name):
        self.name = name

# 使用注册表
for class_name, cls in registry.items():
    print(f"Registered class: {class_name} -> {cls}")        

输出:

Registered class: Widget -> <class '__main__.Widget'>
Registered class: Gadget -> <class '__main__.Gadget'>

这个例子中,每当定义一个类时,装饰器 register_class 自动将类注册到一个名为 registry 的字典中,这样可以在程序的其他部分方便地查找和使用这些类。

注意:在此例子中类在定义时就被装饰器处理了,并没有实例化再处理,这是因为装饰器在Python中是在定义时即可执行的,不需要等到类的实例被创建。
装饰器装饰类进行注册(字典赋值)的过程,就相当于手动创建“装饰器”,如,上述案例等价于:

registry = {}

def register_class(target_class):
    registry[target_class.__name__] = target_class
    return target_class

# @register_class#不使用装饰器,而是手动实现和应用类装饰器
class Widget:
    def __init__(self, name):
        self.name = name

# @register_class
class Gadget:
    def __init__(self, name):
        self.name = name

#手动实现和应用类装饰器#不使用装饰器,而是手动实现和应用类装饰器
Widget=register_class(Widget)
Gadget=register_class(Gadget)

# 使用注册表
for class_name, cls in registry.items():
    print(f"Registered class: {class_name} -> {cls}")

#输出:
#Registered class: Widget -> <class '__main__.Widget'>
#Registered class: Gadget -> <class '__main__.Gadget'>

2.3接口强制

确保类实现了某些必需的方法或属性,如果没有实现,则在类定义时立即抛出错误,例:

def enforce_interface(*required_methods):
    """确保装饰的类实现了所有指定的方法"""
    def decorator(cls):
        for method in required_methods:
            if not hasattr(cls, method):
                raise TypeError(f"类 {cls.__name__} 必须实现方法 {method}")
            if not callable(getattr(cls, method)):
                raise TypeError(f"{cls.__name__} 中的 {method} 必须是可调用的")
        return cls
    return decorator

# 使用装饰器
@enforce_interface('start', 'stop', 'restart')
class MyService:
    def start(self):
        print("服务已启动")

    def stop(self):
        print("服务已停止")

    def restart(self):
        print("服务正在重启")

# 以下类定义将会失败,因为它没有实现 restart 方法
@enforce_interface('start', 'stop', 'restart')
class IncompleteService:
    def start(self):
        print("服务已启动")

    def stop(self):
        print("服务已停止")

运行后输出报错,其中有错误:

    raise TypeError(f"类 {cls.__name__} 必须实现方法 {method}")
TypeError: 类 IncompleteService 必须实现方法 restart

强制接口实现的功能,不需要进行实例化就可以做到,在类定义时即可完成检查报错。
如果手动实现应用此装饰器,相当于:

MyService_detector=enforce_interface('start', 'stop', 'restart')(MyService)
IncompleteService_detector=enforce_interface('start', 'stop', 'restart')(IncompleteService)

2.4增强或修改类的构造函数

装饰器可以用来增强或修改类的构造函数,这通常涉及到在创建实例时添加额外的处理逻辑,或者修改实例的初始化行为。
例,给一个类的构造函数增加“实例创建时间”的功能:

import datetime

def add_timestamp(cls):  # 被装饰的类cls作为参数传入装饰器函数中
    class WrapperClass(cls):  # 包装器类继承被装饰类cls
        def __init__(self, *args, **kwargs):  # 定义包装类的构造函数
            self._created_at = datetime.datetime.now()
            super().__init__(*args, **kwargs)  # 保证了:子类创建实例时,父类的构造函数正常执行

        @property  # python内置装饰器的一种。把create_at函数变为属性(使用时类名.create_at即可,不要再写成 类名..create_at() ),通过create_at获取私有变量_created_at
        def created_at(self):
            return self._created_at

    return WrapperClass

@add_timestamp
class MyService:
    def __init__(self, name):
        self.name = name
# 使用装饰过的类创建实例
service = MyService("Test Service")
print(f"Service created at: {service.created_at}")

输出为:

Service created at: 2024-06-17 23:53:12.250470

通过装饰器函数add_timestamp,配合语法糖@add_timestamp,可以轻易的为某个类增加一个功能:实例创建时间。

如果不使用语法糖@add_timestamp,那么就需要手动实现装饰器的功能。上面的案例,相当于:

#手动实现函数的add_timestamp的装饰器功能
service = add_timestamp(MyService)("Test Service")
print(f"Service created at: {service.created_at}")

2.5属性验证和设置

装饰器可以用来实现属性验证和设置,确保给类的属性赋值时遵循特定的规则。内置装饰器,即属性装饰器可以捕获属性的设置和获取过程,从而在赋值前后执行特定的逻辑。
假设有一个 Person 类,想确保其 age 属性总是非负的,并且 email 属性总是符合电子邮件格式。则可以使用装饰器来实现这些功能。例:

def validate_email(email):
    import re
    if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
        raise ValueError(f"Invalid email format: {email}")

def non_negative(value):
    if value < 0:
        raise ValueError("Value cannot be negative")
    return value

class Person:
    def __init__(self, name, age, email):
        self.name = name
        self._age = age
        self._email = email
    
    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        self._age = non_negative(value)
    
    @property#@property 装饰器使得我们可以将类的方法当作属性一样被访问。
    def email(self):
        return self._email

    @email.setter#@<propertyname>.setter 装饰器允许我们定义设置属性值时的行为,比如进行验证或修改存储的值。
    def email(self, value):
        validate_email(value)
        self._email = value

# 使用示例
p = Person("Alice", 30, "alice@example.com")
print(p.age)  # 30
print(p.email)  # alice@example.com

p.age = -1  # 将引发 ValueError
p.email = "not-an-email"  # 将引发 ValueError

@property,@<propertyname>.setter两种装饰器广泛应用于需要对属性访问或赋值进行控制的情况,如数据验证、自动转换、条件触发等。

2.6内置装饰器的简述

除了上面提到的几类内置装饰器外,还有很多别的内置装饰器,有时候接触一些项目时或许会用到。
如,@classmethod用于标记一个方法为类方法,这个方法可以直接通过类来调用,而不是类的实例。:

class ExampleClass:
    counter = 0

    def __init__(self):
        ExampleClass.counter += 1

    @classmethod
    def get_count(cls):
        return cls.counter

# 创建实例
ex1 = ExampleClass()
ex2 = ExampleClass()
ex3 = ExampleClass()
ex3.counter = 5
#输出实例属性
print(ex3.counter)#5
# 通过类调用类方法
print(ExampleClass.get_count())#3
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值