python 装饰器1 理解到深入【类装饰器】【装饰器是类】
- 汇总
# 0. 装饰器的参数
@deprecated(1,2) # 参数是1,2
def a_test():
pass
@deprecated # 参数是下面的函数
def a_test():
pass
def deprecated(substitute, hint=SSH_PROCESS_HINT):
def deprecation_decorator(func):
func_name = func.__name__
deprecation_message = DEPRECATION_MESSAGE.format(feature=func_name,
substitute=substitute,
hint=hint)
@wraps(func)
def wrapper(*args, **kwargs):
trace = '{}:{}'.format(*_find_trace())
logger.warning(deprecation_message.format(trace=trace))
return func(*args, **kwargs)
return wrapper
return deprecation_decorator
# 1. 装饰器@wrapper 用在定义的地方【函数,类】
@wrapper
def func():
pass
@wrapper
class Person:
sex = 'man'
# 2. 不能直接变量上面
@wrapper
test
# 3. 嵌套定义函数的坑
def check_used_time(timeout):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
start_time = time.time()
org_res = func(*args, **kw)
end_time = time.time() # 会先执行后面的print,但是执行print的时候这里还没有执行到(函数只是执行声明)
return org_res
return wrapper
print(end_time-start_time) # 会显示 name 'end_time' is not defined
return decorator
- 常规装饰器
# 0 最简单的装饰器【这样写无法在函数后执行内容】
def wrap(func): # 参数function,修饰函数
return func # 返回原来的函数
@wrap
def atest(str):
print(str)
atest('hello')
# 1. 【def inner -> 返回 inner】
def wrapper(func): # 装饰器函数,func为被装饰函数
def inner(*args,**kwargs): # 被装饰后的函数可以的参数形式
"""被装饰函数前需要添加的内容"""
ret=func(*args,**kwargs) #被装饰函数
"""被装饰函数后需要添加的内容"""
return ret
return inner
# 2. 带参数装饰器(方法1)
def out_wrapper(flag):
def wrapper(func): #装饰器函数,func为被装饰函数
def inner(*args,**kwargs):
"""被装饰函数前需要添加的内容"""
ret=func(*args,**kwargs) #被装饰函数
"""被装饰函数后需要添加的内容"""
return ret
return inner
return wrapper
# 3. 带参数装饰器(方法2)(采用类的方法)
class allow_count:
def __init__(self, count):
self.count = count
self.i = 0
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kw):
if self.i >= self.count:
return
self.i += 1
return func(*args, **kw)
return wrapper
- 类装饰器(装饰类的)
# 装饰器 【cls 参数 -> 返回 cls】
def attr_upper(cls):
for attrname,value in cls.__dict__.items():
if isinstance(value,str):
if not value.startswith('__'):
setattr(cls,attrname,bytes.decode(str.encode(value).upper()))
return cls
# 函数定义
@attr_upper
class Person:
sex = 'man'
# 效果
print(Person.sex) # MAN
- 装饰器(装饰器是类)
# 原理:
# 【func参数 -> 返回 func的result】
# __call__ 方法可以将实例对象编程可调用对象
# 装饰器是一个语法糖相当于 func = cache(func)
# 凡是可以将 () 直接应用到自身并执行,都称为可调用对象。对于可调用对象,实际上“名称()”可以理解为是“名称.__call__()”的简写
class CLanguage:
def __call__(self, *args, **kwargs):
print('__call__ :',args,kwargs)
c = CLanguage()
c('11','22','33',d='44')
# 实际应用1 (缓存装饰器)
class Cache:
def __init__(self, func):
self.func = func
self.data = {} # 很巧妙,不init就不会执行这里的代码
def __call__(self, *args, **kwargs):
func = self.func
data = self.data
key = f'{func.__name__}-{str(args)}-{str(kwargs)})'
if key in data:
result = data.get(key)
print('cached')
else:
result = func(*args, **kwargs)
data[key] = result
print('calculated')
return result
@Cache
def rectangle_area(length, width):
return length * width
rectangle_area(2, 3)
# calculated
# 6
rectangle_area(2, 3)
# cached
# 6
# 实际应用2 (限制执行次数)
import functools
class allow_count:
def __init__(self, count):
self.count = count
self.i = 0
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kw):
if self.i >= self.count:
return
self.i += 1
return func(*args, **kw)
return wrapper
@allow_count(3)
def job(x):
x += 1
return x
for i in range(5):
print(job(i))
# 结果:
#
# 1
# 2
# 3
# None
# None
# 实际应用3 (robot中)(这里没有使用__call__的原理,使用了描述符类的概念)
class setter(object):
def __init__(self, method):
self.method = method
self.attr_name = '_setter__' + method.__name__
self.__doc__ = method.__doc__
def __get__(self, instance, owner):
if instance is None:
return self
try:
return getattr(instance, self.attr_name)
except AttributeError:
raise AttributeError(self.method.__name__)
def __set__(self, instance, value):
if instance is None:
return
setattr(instance, self.attr_name, self.method(instance, value))
@setter
def keywords(self, keywords):
"""Suite setup and teardown as a :class:`~.Keywords` object."""
return Keywords(self.keyword_class, self, keywords)
- 类写的装饰器来装饰类的方法
https://zhuanlan.zhihu.com/p/44667584
# 要用类写的装饰器来装饰类的方法,只需要把可调用对象包装成函数就行。
# 定义一个简单的装饰器,什么也不做,仅仅是把可调用对象包装成函数
def method(call):
def wrapper(*args, **kwargs):
return call(*args, **kwargs)
return wrapper
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@method
@Cache
def area(self):
return self.length * self.width
r = Rectangle(2, 3)
r.area()
# calculated
# 6
r.area()
# cached
# 6
或者用@property还能直接把方法变成属性。
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@property
@Cache
def area(self):
return self.length * self.width
r = Rectangle(2, 3)
r.area
# calculated
# 6
r.area
# cached
# 6
其他人写的博客:
https://blog.csdn.net/lht0909/article/details/100715749