装饰器
原理
在不修改原功能函数的基础上而扩展新的功能
目标
- 怎么写、使用装饰器?
- 什么时候需要使用装饰器?
装饰器类
通常为装饰器函数,但是装饰器函数内一般只能有1个包装函数,因此,如果想在装饰器内调用另外的函数,需要使用装饰器类
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile并写入
with open(self.logfile, 'a') as opened_file:
# 现在将日志打到指定的文件
opened_file.write(log_string + '\n')
# 现在,发送一个通知
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
# logit只打日志,不做别的
pass
@logit()
def myfunc1():
pass
@wraps 装饰器
作用
消除装饰器的副作用,保留被装饰函数的原有属性。
from functools import wraps
def decorator(func):
@wraps(func)
def fun_test():
"""fun_test"""
print("fun_test")
return fun_test
@decorator
def fun():
"""fun """
print("fun")
# fun = decorator(fun)
print(fun.__name__)
fun()
print(fun.__name__)
使用场景
- 权限使用
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated
- 日志使用场景
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)
# Output: addition_func was called
代码解释:
@classmethod 装饰器
使用了@classmethod的函数,可以不用实例化类对象,直接进行调用。第一个参数为类而不是实例。
class MathUtils:
pi = 3.14159
@classmethod
def circle_area(cls, radius):
return cls.pi * radius**2
@staticmethod
def square_area(side_length):
return side_length**2
# 直接通过类调用方法
area = MathUtils.circle_area(5)
print(area) # 输出: 78.53975
@staticmethod 装饰器
@staticmethod: 将方法转换为静态方法,不传递类或实例的引用。静态方法不需要访问类的属性或方法
class MathUtils:
pi = 3.14159
@staticmethod
def add_numbers(num1, num2):
return num1 + num2
@staticmethod
def square_area(side_length):
return side_length**2
# 直接通过类调用静态方法
result = MathUtils.add_numbers(3, 5)
print(result) # 输出: 8
# 创建实例并通过实例调用静态方法
math = MathUtils()
area = math.square_area(4)
print(area) # 输出: 16
@property 装饰器
@property:将方法转换为属性,调用时可以用访问属性的时候直接调用,而不是访问方法的时候加上()
使用场景
-
封装属性的读取和设置方法:使用
@property装饰器可以将类中的属性封装起来,通过定义 getter 和 setter 方法来控制属性的读取和设置行为。这可以帮助保证属性的合法性和一致性,同时提供更好的代码封装和可维护性。 -
计算属性:有时候,某个属性的值并不是直接存储在实例变量中,而是依据其他属性或方法进行计算得到的。在这种情况下,可以使用
@property装饰器将计算逻辑封装在一个方法中,并通过属性的方式来访问计算结果。这样可以实现动态计算属性值,并隐藏底层的计算细节。 -
属性验证和处理:使用
@property装饰器可以在设置属性值时执行验证和处理逻辑,确保属性值满足特定的条件。例如,可以在设置器方法中检查属性值的类型、范围或其他约束,并根据需要执行相应的处理操作。 -
与数据库或外部接口的交互:在与数据库或外部接口进行交互时,有时需要对数据进行一些处理或转换。通过使用
@property装饰器,可以将这些处理逻辑封装在属性的读取和设置方法中,使得读写操作在调用代码层面更简洁和易用。 -
属性的动态计算和更新:在某些情况下,属性的值可能会随着时间、状态或其他因素发生改变。通过将属性定义为带有
@property装饰器的方法,可以实现属性的动态计算和更新。这样,每次访问属性时都会重新计算属性值,以确保属性的精确性和实时性。
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value >= 0:
self._age = value
else:
raise ValueError("Age must be a positive integer.")
# 创建 Person 对象
person = Person("Alice", 25)
# 通过属性方式获取 name 和 age
print(person.name) # 输出: Alice
print(person.age) # 输出: 25
# 通过属性方式设置 name 和 age
person.name = "Bob"
person.age = 30
print(person.name) # 输出: Bob
print(person.age) # 输出: 30
# 尝试设置无效的 age 值会引发 ValueError 异常
person.age = -10 # 抛出异常: ValueError: Age must be a positive integer.
@abstractmethod 装饰器
@abstractmethod 是一个装饰器,用于标记一个方法为抽象方法。抽象方法是指在基类中声明但没有具体实现的方法,需要在派生类中进行实现。它主要用于定义类的接口和规范子类的行为。
注意点:
- 使用abstractmethod 的类必须为抽象类,抽象类必须导入
from abc import ABC, abstractmethod - 抽象类必须继承
ABC - 实现类必须继承抽象类,且必须实现抽象方法
- 抽象类不能进行实例化,而是作为其他类的父类
from abc import ABC, abstractmethod
class MyAbstractClass(ABC):
@abstractmethod
def my_abstract_method(self):
pass
def my_concrete_method(self):
print("This is a concrete method.")
class MyConcreteClass(MyAbstractClass):
def my_abstract_method(self):
print("Implemented abstract method.")
# 实例化具体子类
my_concrete_instance = MyConcreteClass()
my_concrete_instance.my_abstract_method() # 输出: Implemented abstract method.
my_concrete_instance.my_concrete_method() # 输出: This is a concrete method.
@try_except 装饰器
@try_except当处理异常的代码块需要在多个函数或方法中共享时,可以使用装饰器来封装异常处理逻辑。通过创建一个装饰器函数,可以将异常处理逻辑应用于特定的函数或方法,以避免在每个函数或方法中重复编写相同的异常处理代码。
def handle_exception(func):
def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
return result
except ZeroDivisionError:
print("除零错误发生")
except FileNotFoundError:
print("文件未找到")
except Exception as e:
print("发生了一个异常:", str(e))
return wrapper
@handle_exception
def divide_numbers(num1, num2):
result = num1 / num2
print("结果:", result)
@handle_exception
def read_file(file_path):
with open(file_path, "r") as file:
contents = file.read()
print("文件内容:", contents)
# 测试 divide_numbers 函数
divide_numbers(10, 0) # 输出: 除零错误发生
# 测试 read_file 函数
read_file("nonexistent_file.txt") # 输出: 文件未找到