我有一个装饰器,可以装饰不以两个下划线开头的类方法。
def log_methods(date_format):
def decorator(cls):
for attr_name in dir(cls):
attr = getattr(cls, attr_name)
if callable(attr) and not attr_name.startswith('__'):
def wrapper(*args, **kwargs):
start = datetime.now()
print(f'Starting {attr.__qualname__} date and time {start.strftime(date_format)}')
result = attr(*args, **kwargs)
end = datetime.now()
print(f'Finishing {attr.__qualname__}, function operation time: {end.strftime(date_format)}')
return result
setattr(cls, attr_name, wrapper)
return cls
return decorator
@log_methods("%b %d %Y - %H:%M:%S")
class A:
new_class_attr = 2
def test_func(self):
print('test_func called')
a = A()
a.test_func()
我决定找出如果我修饰 class 属性会发生什么。为此,我从条件中删除了
callable(attr)
检查。添加了new_class_attr
属性的装饰器,但是不明白为什么调用的时候会执行test_func
方法?
def log_methods(date_format):
def decorator(cls):
for attr_name in dir(cls):
attr = getattr(cls, attr_name)
if not attr_name.startswith('__'):
def wrapper(*args, **kwargs):
start = datetime.now()
print(f'Starting {attr.__qualname__} date and time {start.strftime(date_format)}')
result = attr(*args, **kwargs)
end = datetime.now()
print(f'Finishing {attr.__qualname__}, function operation time: {end.strftime(date_format)}')
return result
setattr(cls, attr_name, wrapper)
return cls
return decorator
@log_methods("%b %d %Y - %H:%M:%S")
class A:
new_class_attr = 2
def test_func(self):
print('test_func called')
a = A()
a.new_class_attr() # The test_func method is called
正如评论中提到的,你的装饰器(第一个)永远不会正常工作。 让我们用修改后的类来测试它:
from datetime import datetime
# ...
@log_methods("%b %d %Y - %H:%M:%S") # OP 1st decorator
class A:
def test_func(self):
return('test_func called')
def test_func_X(self):
return('test_funcX called')
a = A()
print(a.test_func())
#test_funcX called
print(a.test_func_X())
#test_funcX called
所有可调用函数将指向最后一个(按字母顺序)编辑的函数。您可以使用
inspect.source
进行检查,它将函数主体提取为纯文本。
from inspect import getsource
src1 = getsource(a.test_func)
src2 = getsource(a.test_func_X)
print(src1 == src2)
# True
您需要对方法的装饰器进行进一步参数化
def log_methods(date_format):
def decorator(cls):
def __wrapper(func):
def wrapper(*args, **kwargs):
start = datetime.now()
print(f'Starting {attr.__qualname__} date and time {start.strftime(date_format)}')
result = func(*args, **kwargs) # <-
end = datetime.now()
print(f'Finishing {attr.__qualname__}, function operation time: {end.strftime(date_format)}')
return result
return wrapper
for attr_name in dir(cls):
attr = getattr(cls, attr_name)
if callable(attr) and not attr_name.startswith('__'):
setattr(cls, attr_name, __wrapper(attr)) # <-
return cls
return decorator
对于第二个装饰器,您需要区分可调用和不可调用...否则,
a.new_class_attr()
会得到什么? ...与调用整数相同:2()
!