1.装饰器没有参数:
这时foo不再是之前的函数名而是类ClassDeco的一个对象,并且foo.func=foo,对象名()会触发类ClassDeco的__call__方法:
class ClassDeco:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(f'Running {self.func.__name__}')
self.func()
print(f'End {self.func.__name__}')
@ClassDeco # 等价于 foo = ClassDeco(foo)
def foo():
print('do something')
foo()
"""
Running foo
do something
End foo
"""
2.装饰器有参数,被装饰函数没有参数
类名作为装饰器名,并且括号内有参数。先执行@后面的代码,类名加()形成一个对象,然后对象()触发类中的__call__方法,并且__call__方法中的func参数就是被装饰的函数名:
class ClassDeco:
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self, func, *args, **kwargs):
print(f'Running {func.__name__}')
print(f'Using x + y = {self.x + self.y}')
return func
@ClassDeco(1, 2) # 等价于 foo = ClassDeco(1, 2)(foo)
def foo():
print('do something')
foo()
"""
Running foo
Using x + y = 3
do something
"""
3.类装饰器不带参数,被包装对象带参数
类装饰器不带参数,就会直接将被装饰的函数名当做参数传入装饰器类的()中,触发装饰器类的__init__方法。此时的函数名是装饰器类的一个对象,这时的函数如果加括号调用,就会触发装饰器类当中的__call__方法,并且将参数传入到__call__方法当中的形参:
class ClassDeco:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(f'Running {self.func.__name__}')
self.func(*args, **kwargs)
print(f'End {self.func.__name__}')
@ClassDeco # 等价于foo = ClassDeco(foo)
def foo(a, b):
print('do something')
print(f'return a + b = {a + b}')
foo(1,2)
"""
Running foo
do something
return a + b = 3
End foo
"""
4.类装饰器带参数且被装饰对象也带参数
from functools import wraps
class ClassDeco:
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self, func, *args, **kwargs):
print(f'Using x + y = {self.x + self.y}')
@wraps(func)
def wrapper(*args, **kwargs):
print('func',func.__name__)
func(*args, **kwargs)
print(f'Ending {func.__name__}')
return wrapper
@ClassDeco(1, 2) # (1)
def foo(a, b):
print('do something')
print(f'return a + b = {a + b}')
foo(5,8) # (2)
# 执行结果:
"""
Using x + y = 3
func foo
do something
return a + b = 13
Ending foo
"""
(1) 这行代码相当于:foo = ClassDeco(1,2)(foo),执行过程中ClassDeco(1,2)是先生成一个ClassDeco对象,此时会执行__init__方法。再执行对象(foo),会触发__call__方法,并且会将函数名foo当做参数传入到__call__中,所以会首先执行print(f'Using x + y = {self.x + self.y}'),并且self.x = 1,self.y = 2。此时定义了函数wrapper,并且把wrapper返回给foo,所以此时的foo就是wrapper,但是此时的wrapper并没有执行。
(2) 函数名加括号,此时的函数名是装饰器类ClassDeco中__call__的返回值wrapper,所以函数名加括号就相当于wrapper(5,8)。在wrapper中func(*args,**kwargs)才是真正执行foo(5,8)的代码