装饰器是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