官网文档
https://docs.python.org/zh-cn/3/reference/compound_stmts.html#function-definitions
https://docs.python.org/zh-cn/3/reference/compound_stmts.html#class-definitions
python工匠
https://pengzhangzhi.github.io/one-python-craftsman/zh_CN/8-tips-on-decorators.html
多装饰器顺序
-
导入时输出111 444 555 222,运行时输出333 666。
- test == test1()(test2()(test)) 为 True
- 导入时
- test1()(test2()(test)),首先执行test1(),再执行test2(),得到dec1(dec2(test))。
- 然后执行dec1(dec2(test)),首先执行dec2(test),再执行dec1(),得到wrapper1,此时wrapper1中func1即为wrapper2
- 运行时
- 执行wrapper1,返回func1()即wrapper2()
- wrapper2()运行,返回真正的test()
def test1(): print(111) def dec1(func): print(222) def wrapper1(): print(333) return func1() return wrapper1 return dec1 def test2(): print(444) def dec2(func): print(555) def wrapper2(): print(666) return func2() return wrapper2 return dec2 @test1() @test2() def test(): print(777) # test == test1()(test2()(test)) # True test()
-
导入时顺序输出222 111,运行时输出333,注意**test == dec1(dec2(test))**为True
def dec1(func): print(111) return func def dec2(func): print(222) return func @dec1 @dec2 def test(): print(333) # test == dec1(dec2(test)) # test()
-
导入时顺序输出111 333 444 222。注意test == dec1()(dec2()(test))为True
def dec1(): print(111) def wrapper(func): print(222) return func return wrapper def dec2(): print(333) def wrapper(func): print(444) return func return wrapper @dec1() @dec2() def test(): print(555) # test == dec1()(dec2()(test))
-
变式
def test(func): print(222) def dec(): func() return dec @test def a(): print(111)
实用例子
import time
class Timeit:
def __init__(self, func=None, arg0=None):
self._func = func
self.arg0 = arg0
self.calls = 0
def __call__(self, *args, **kwargs):
print(f'参数1:{self.arg0}')
if self._func:
self.calls += 1
start_time = time.time()
r0 = self._func(*args, **kwargs)
print(f'耗时:{time.time() - start_time}')
print("调用 %s() %d 次" % (self._func.__name__, self.calls))
return r0
else:
func = args[0]
def wrapper(*ars, **kws):
self.calls += 1
start_time1 = time.time()
r1 = func(*ars, **kws)
print(f'耗时:{time.time() - start_time1}')
print("调用 %s() %d 次" % (func.__name__, self.calls))
return r1
return wrapper
@Timeit
def foo1(sleep_time):
print(f'睡眠{sleep_time}秒')
time.sleep(sleep_time)
# fool1 = Timeit(fool1)
@Timeit(arg0=99999)
def foo(sleep_time):
print(f'睡眠{sleep_time}秒')
time.sleep(sleep_time)
# foo = Timeit(arg0=99999)(foo)
if __name__ == '__main__':
foo1(3)
foo1(2)
for _ in range(3):
foo(1)
装饰器实现对象\实例的替换
from functools import update_wrapper
class DelayedStart:
def __init__(self, func):
update_wrapper(self, func) # 和 @functools.wraps(func) 一致
self.func = func
def __call__(self, *args, **kwargs):
print("Wait for 1 sec before starting...")
time.sleep(1)
self.func(*args, **kwargs)
def eager_call(self, *args, **kwargs):
print("Call without delay")
self.func(*args, **kwargs)
@DelayedStart
def hello():
print("Hello, world.")
####################
# 输出
####################
>>hello
<__main__.DelayedStart object at 0x04793310>
>>type(hello)
<class '__main__.DelayedStart'>
>>hello.__name__ 被包装函数的元数据更新到包装者上
'hello'
>>hello()
Wait for 1 sec before starting...
Hello, world.
>>hello.eager_call() 由上述得hello对象替换为了DelayedStart,就可以调用eager_call方法
Call without delay
Hello, world.
装饰器注入参数
import random
from functools import wraps
def provide_num(min_num, max_num):
"""装饰器,随机生成[min_num, max_num]范围内的整数,并注入函数的第一个参数"""
def inner(func):
@wraps(func)
def wrapper(*args, **kwargs):
num = random.randint(min_num, max_num)
if isinstance(args[0], int):
args = (args[0] + num, )
else:
args = (args[0], args[1] + num)
func(*args, **kwargs)
return wrapper
return inner
@provide_num(1, 10)
def print_num(num):
print(num)
class Foo:
@provide_num(1, 10)
def print_num(self, num):
print(num)
print_num(777)
Foo().print_num(777)
如果使用这样的装饰器则num的传参会覆盖类方法的第一个默认参数self
def provide_num_error(min_num, max_num):
"""装饰器,随机生成[min_num, max_num]范围内的整数,并注入函数的第一个参数"""
def wrapper(func):
@wraps(func)
def dec(*args, **kwargs):
num = random.randint(min_num, max_num)
func(num, *args, **kwargs)
return dec
return wrapper
细品
def foo():
print(222)
def dec(func):
return func
return dec
@foo()
def a():
print(111)
a()
def foo(func):
print(222)
def dec():
return func()
return dec
@foo
def a():
print(111)
a()
"""
222
111
"""
def foo(func):
print(222)
def dec():
return func
return dec
@foo
def a():
print(111)
a()
"""
222
"""
class Foo:
def register(self, check, *tags):
def inner(check):
return check
if callable(check):
return inner(check)
else:
if check:
tags += (check,)
return inner
foo = Foo()
@foo.register("my_tag")
def demo(aaa):
print(aaa)
"""
运行时 -> foo.register("my_tag")(demo)
此时check为"my_tag",返回inner
-> inner(demo)
"""