class logger(object):
logs = {}
def __init__(self, func):
self.func = func
if func.__name__ not in self.logs:
self.logs[func.__name__] = 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))
@logger
def say1(something):
print("say1 {}!".format(something))
say("hello")
say1("hello")
当使用@logger装饰器时,Python会执行say = logger(say),这会创建一个logger类的实例(即logger_instance),并将原函数say作为参数传递给__init__方法。这个实例会被赋值给say,因此后续调用say(“hello”)实际上是在调用这个
在Python中,当使用类作为装饰器时(如@logger),其执行流程遵循装饰器的底层机制和类的特殊方法调用规则。以下是关键原因分析:
1、 @logger时执行__init__的原因
当使用@logger语法时,Python会立即执行装饰器类的初始化,相当于:
say = logger(say) # 触发__init__
当执行@logger时,相当于:
say = logger(say) # 创建logger类的实例,并赋值给say
logger(say)会调用logger.__init__,生成一个logger类的实例(即logger_instance)。
此时say不再是原函数,而是logger类的实例。
装饰器本质:装饰器语法糖会将目标函数(如say)作为参数传递给装饰器类,此时会调用类的__init__方法。
初始化作用:__init__用于保存被装饰的函数(self.func = func)或执行其他初始化逻辑(如注册函数到logs字典)。
2、say()时调用__call__的原因
当调用被装饰的函数(如say(“hello”))时,实际调用的是装饰器类的实例,而类实例的调用行为由__call__方法定义:
可调用对象:实现了__call__的类实例可以像函数一样被调用。
执行流程:
say("hello") # 等价于 logger_instance.__call__("hello")
__call__方法会在每次调用时执行,包裹原函数(如打印日志、执行self.func(*args, **kwargs))
在Python中,如果一个类实现了__call__方法,它的实例就可以像函数一样被调用。
当执行say(“hello”)时,Python会隐式调用logger_instance.__call__(“hello”),这是__call__方法的特性。
# 有参数的类装饰器
if 0:
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 say(something):
print("say {}!".format(something))
say("hello")
if 1:
# 其实相当于下面这种写法
def logger(level): # 外层相当于__init__
def decorator(func): # 相当于__call__
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
@logger(level='WARNING') # 构造传递参数
def say(something):
print("say {}!".format(something))
say("hello")
if 1:
# 借助partial实现可以达到上面同样的效果
import time
import functools
class DelayFunc:
def __init__(self, level, func):
self.level = level
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
def delay(level):
# 此处为了避免定义额外函数,
# 直接使用 functools.partial 帮助构造 DelayFunc 实例,可以传递参数并避免__call__中额外定义函数
return functools.partial(DelayFunc, level)
@delay(level='WARNING') # 构造传递参数
def say(something):
print("say {}!".format(something))
say("hello")