Python中装饰器的用法总结:
'''
Author: Hollis23
Date: 2021-10-19 15:01:29
LastEditTime: 2021-10-20 09:27:56
LastEditors: Hollis23
Description: v0.0
FilePath: /mmdetection/tests/test_decorator/1.py
'''
import functools
import time
from typing import AsyncGenerator
# ------------------------------------------------------
# 1.装饰器语法糖
# ------------------------------------------------------
'''
@符号就是装饰器语法糖
它放在一个函数开始定义的地方,就像一顶帽子一样戴在这个函数的头上,和这个
函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是
将这个函数作为参数传入他头顶这顶帽子,这顶帽子我们称为装饰函数或装饰器
'''
# -------------------------------------------------------
# 2.装饰器入门用法
# -------------------------------------------------------
def logger(func):
def wrapper(*args, **kw):
print('Prepare to calculate: {} func!'.format(func.__name__))
# truly process
func(*args, **kw)
print('claculate Done!')
return wrapper
@logger
def add(x, y):
print('{} + {} = {}'.format(x, y, x+y))
# 时间计时器
def timer(func):
def wrapper(*args, **kw):
t1 = time.time()
func(*args, **kw)
t2 = time.time()
# 计算时长
cost_time = t2 - t1
print('take {} seconds'.format(t2 - t1))
return wrapper
@timer
def want_sleep(sleep_time):
time.sleep(sleep_time)
# if __name__ == '__main__':
# add(200, 50)
# want_sleep(10)
# -------------------------------------------------------
# 3.带参数的装饰器函数
# -------------------------------------------------------
def say_hello(country): # 传装饰器带的参数
def wrapper(func): # 传函数
def deco(*args, **kw): # 传函数带的参数
if country == 'china':
print('你好!')
elif country == 'american':
print('Hello!')
else:
return
# truely process func
func(*args, **kw)
return deco
return wrapper
@say_hello('american')
def american():
print('I coming from America!')
@say_hello('china')
def china():
print('I coming from China!')
# if __name__ == '__main__':
# china()
# american()
# -------------------------------------------------------
# 4.不带参数的类装饰器
# -------------------------------------------------------
# 基于类的装饰器的实现,必须实现__call__和__init__两个内置函数,其中
# __init__: 接收被装饰函数
# __call__: 实现装饰逻辑
class logger(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('[INFO]:the function {func}() is running...'
.format(func=self.func.__name__))
return self.func(*args, **kwargs)
@logger
def say(something):
print('say {}!'.format(something))
# if __name__ == '__main__':
# say('hello')
# -------------------------------------------------------
# 5.带参数的类装饰器
# -------------------------------------------------------
'''
上面不带参数的例子只能打印INFO级别的日志,正常情况下,我们还要打印
DEBUG,WARNING等级别的日志。这就需要给类装饰器传入参数,给这个函数
指定级别了。
带参数和不带参数的装饰器有很大的不同:
__init__: 不再接收被装饰函数,而是接收传入参数
__call__: 接收被装饰函数,实现装饰逻辑
'''
class logger(object):
def __init__(self, level='INFO'):
self.level = level
def __call__(self, func):
def wrapper(*args, **kwargs):
print('[{level}]: the function {func}() is running...'
.format(level=self.level, func=func.__name__))
func(*args, **kwargs)
return wrapper
@logger(level='WARNING')
def talk(something):
print('say {}!'.format(something))
if __name__ == '__main__':
talk('hello')
# -------------------------------------------------------
# 6.使用偏函数与类实现装饰器
# -------------------------------------------------------
'''
绝大多数装饰器都是基于函数和闭包实现,但这并不是制造装饰器的唯一方法
Python对某个对象是否能通过装饰器形式使用(当做装饰器使用)只有一个要求:decorator必须
是一个‘可被调用(callable)的对象’。
这个callable对象,我们最熟悉的就是函数,类也可以是callable对象,只要实现了__call__函数
,还有比较少人使用的偏函数也是callable对象。
'''
# 实现了__call__的类
class DelayFunc:
def __init__(self, duration, func):
self.duration = duration
self.func = func
def __call__(self, *args, **kwargs):
print(f'Waig for {self.duration} seconds...')
time.sleep(self.duration)
return self.func(*args, **kwargs)
def eager_call(self, *args, **kwargs):
print('Call without delay')
return self.func(*args, **kwargs)
# delay是一个偏函数,这里delay就可以作为一个装饰器
def delay(duration):
return functools.partial(DelayFunc, duration)
@delay(duration=2)
def add(a, b):
return a + b
# if __name__ == '__main__':
# add(3, 5)
# -------------------------------------------------------
# 7.如何写能装饰类的装饰器?
# -------------------------------------------------------
# 用python写单例模式的时候,常用的有三种写法。其中一种是用装饰器来实现的
# 如,装饰器版的单例模式
instances = {}
def singleton(cls):
def get_instance(*args, **kwargs):
cls_name = cls.__name__
print('============1============')
if not cls_name in instances:
print('++++++++2+++++++++++')
instance = cls(*args, **kwargs)
instances[cls_name] = instance
return instances[cls_name]
return get_instance
@singleton
class User:
_instance = None
def __init__(self, name):
print('===========3==========')
self.name = name
# 装饰器用在类上,并不是很常见,但只要熟悉装饰器的实现过程,就不难实现对类的装饰。这里
# 的装饰器就只是实现对类实例的生成的控制而已
# if __name__ == '__main__':
# u1 = User('hollis')
# u1.age = 20
# print(u1)
# print(instances)
# u2 = User('hollis2')
# print(u2.age)
# -------------------------------------------------------
# 8.warp装饰器有啥用?
# -------------------------------------------------------
# 在functools中提供一个warps装饰器,有啥用?
# 它的作用就是将被修饰的函数(wrapped)的一些属性值赋值给修饰器函数(wrapper),最终让属性的显示
# 更符合我们的直觉
# -------------------------------------------------------
# 9.内置装饰器property
# -------------------------------------------------------
# property通常存在于类中,可以将一个函数定义成一个属性,属性的值就是该函数return的内容
# (1)常规创建类并赋值的方法:有风险,并不能对属性的值做合法性限制
class Student(object):
def __init__(self, name, age=None):
self.name = name
self.age = age
# 实例化
XiaoMing = Student('小明')
# 添加属性
XiaoMing.age = 25
# 查询属性
XiaoMing.age
# 删除属性
del XiaoMing.age
# (2)私有属性写法
class Student(object):
def __init__(self, name):
self.name = name
self.name = None
def set_age(self, age):
if not isinstance(age, int):
raise ValueError('输入不合法:年龄必须为数值!')
if not 0 < age < 100:
raise ValueError('输入不合法:年龄范围必须0-100!')
self._age = age
def get_age(self):
return self._age
def del_age(self):
self._age = None
XiaoMing = Student('小明')
XiaoMing.set_age(25)
XiaoMing.get_age()
XiaoMing.del_age()
# (3)使用property装饰器将函数变为属性
class Student(object):
def __init__(self, name):
self.name = name
# self.name = None
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if not isinstance(value, int):
raise ValueError('年龄必须为数字')
if not 0 < value < 100:
raise ValueError('年龄超出正常范围')
self._age = value
@age.deleter
def age(self):
del self._age
# if __name__ == '__main__':
# XiaoMing = Student('小明')
# XiaoMing.age = 25
# print(XiaoMing.age)
# del XiaoMing.age
# print(XiaoMing.name)