简介
- 装饰器:在不修改被装饰函数的代码情况下,增强其功能。这个类似于Java中的面向切片编程。它本质也是一个函数,接受一个被装饰函数作为参数,返回增强版的与被装饰函数同名的函数
- 闭包是装饰器的基础,python中的闭包详见python之函数与闭包
装饰器的实现
- 装饰器的语法如下:
- 使用语法糖:在被装饰的函数之前加上@装饰器函数名或者在被装饰的类之上加上@装饰器类名。这个常用
@decorator
def decorated_function():
pass
- 显示的调用装饰器。如果在一个函数上需要使用多个装饰器,这种写法可读性差
def decorated_function():
pass
decorated_function = decorator(decorated_function)
- 用函数实现装饰器。
- 简单的示例如下所示:
# 需求,给login接口增加日志记录的功能
from loguru import logger
def my_decorator(function):
'''
function为被装饰函数
'''
def wrapped(*args, **kwargs):
# 被装饰函数调用之前执行的操作
logger.info(f'日志记录开始')
# 执行被装饰函数
ret = function(*args, **kwargs)
# 被装饰函数调用之后执行的操作
logger.info(f'日志记录结束')
return ret
return wrapped
def login(user_name, password):
if user_name == 'admin' and password == '123456':
print('login success')
else:
print('login failed')
# 显式的调用装饰器
login = my_decorator(login)
login('admin', '123456')
- 带参数的装饰器:在装饰器之上再套一个函数
# 需求:现有一个登录接口,1. 需要增加日志功能登录接口 2. 可以支持微信和QQ登录
from loguru import logger
def my_decorator_plus(type):
def my_decorator(function):
def wrapper(*args, **kwargs):
# 微信登录
if type == 0:
logger.info(f'微信登录日志记录开始')
ret = function(*args, **kwargs)
logger.info(f'微信登录日志记录结束')
# QQ登录
elif type == 1:
logger.info(f'QQ登录日志记录开始')
ret = function(*args, **kwargs)
logger.info(f'QQ登录日志记录结束')
return ret
return wrapper
return my_decorator
@my_decorator_plus(0)
def login(user_name, password):
if user_name == 'admin' and password == '123456':
print('login success')
else:
print('login failed')
login('admin', '123456')
- 自定义类实现装饰器。
- __call__方法介绍:这个方法可以将类的实例化对象转化成可调用对象。可调用对象可以直接调用__call__方法或者以
对象名称()
的形式调用。示例如下:
class Student:
def __init__(self, name):
super().__init__()
self.name = name
# 定义__call__方法后,Student类对象实例化的对象成为一种可调用对象
def __call__(self):
print('hi,' + f'{self.name}')
stu = Student("zss")
print(callable(stu)) # 检查一个对象是否可调用,True
stu.__call__() # hi,zss
stu() # hi,zss
- 实现的类装饰器如下所示
from typing import Any
from loguru import logger
class MyDecorator:
def __init__(self, function) -> None:
self.function = function
def __call__(self, *args: Any, **kwds: Any) -> Any:
# 被装饰函数调用之前执行的操作
logger.info('日志记录开始')
# 执行被装饰函数
ret = self.function(*args, **kwds)
# 被装饰函数调用之后执行的操作
logger.info(f'日志记录结束')
return ret
# 使用语法糖使用装饰器
@MyDecorator
def login(user_name, password):
if user_name == 'admin' and password == '123456':
print('login success')
else:
print('login failed')
login('admin', '123456')
装饰器的应用场景
1.在方法定义的开头能够将其定义为类方法或者静态方法
- 不使用装饰器前,手动将函数转化为类方法或者静态方法
class Test:
def clsFunc(cls):
print("this is class Method")
def staFunc():
print("this is static Method")
# 将函数转化为类方法
clsMethod = classmethod(clsFunc)
# 将函数转化为静态方法
staMethod = staticmethod(staFunc)
Test.clsMethod()
Test.staMethod()
- 使用装饰器后,方便将函数转化为类方法或者静态方法。如下示例使用了
@classmethod
和@staticmethod
两个装饰器,被装饰函数分别为clsFunc和staFunc
class Test:
@classmethod # 等同于clsFunc = classmethod(clsFunc)
def clsFunc(cls):
print("this is class Method")
@staticmethod # 等同于staFunc = staticmethod(staFunc)
def staFunc():
print("this is static Method")
Test.clsFunc()
Test.staFunc()
2.上下文装饰器
- 上下文装饰器确保函数可以运行在正确的上下文中,或者在函数前后运行一些代码。
from threading import RLock
lock = RLock()
def synchronized(function):
def _synchronized(*args, **kw):
lock.acquire()
try:
return function(*args, **kw)
finally:
lock.release()
return _synchronized
@synchronized
def thread_salf():
pass
# 上述程序的缺点:如果函数的执行逻辑比较多,则这个方法加锁的粒度大
3.@property装饰器
- 装饰器:在代码运行期间动态增加函数或者方法。在类中的方法使用
@property
修饰,则会将该属性作为类属性,可以像属性一样的方式通过对象.方法名
或者类名.方法名
来访问方法。
- 示例如下:
class Student:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
stu = Student("zss")
print(stu.name) # zss