import inspect
import functools
def for_all_test_methods(decorator):
def decorate(cls):
for name, value in inspect.getmembers(cls, inspect.isroutine):
if name.startswith('test'):
setattr(cls, name, test_decorator(getattr(cls, name)))
return cls
return decorate
def test_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(func.__name__, args, kwargs)
res = func(*args, **kwargs)
return res
return wrapper
@for_all_test_methods(test_decorator)
class Potato(object):
def test_method(self):
print('in method')
class Spud(Potato):
def test_derived(self):
print('in derived')
现在,如果我创建一个spud实例,它继承的test_method
仍然会被装饰,但它有一个未修饰的方法test_derived
。不幸的是,如果我将类装饰器添加到Spud
上,那么他的test_method
会被装饰两次!
如何正确地将装饰器从父类传播到子类上?
答案 0 :(得分:2)
你无法避免装饰派生类;您可以在子类装饰后找到类的子类,但不能自动装饰它们。使用元类代替您需要这种行为。
你可以做以下两件事之一:
检测已装饰的方法;如果有一个__wrapped__
属性,你有一个包装器:
def for_all_test_methods(decorator):
def decorate(cls):
for name, value in inspect.getmembers(cls, inspect.isroutine):
if name.startswith('test') and not hasattr(value, '__wrapped__'):
setattr(cls, name, test_decorator(getattr(cls, name)))
return cls
return decorate
仅将类装饰器限制为直接方法:
def for_all_test_methods(decorator):
def decorate(cls):
for name, value in cls.__dict__.iteritems():
if name.startswith('test') and inspect.isroutine(value)):
setattr(cls, name, test_decorator(getattr(cls, name)))
return cls
return decorate
答案 1 :(得分:1)
以下是使用元类而不是装饰类来实现此目的的方法:
import inspect
import functools
def test_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(func.__name__, args, kwargs)
res = func(*args, **kwargs)
return res
return wrapper
def make_test_deco_type(decorator):
class TestDecoType(type):
def __new__(cls, clsname, bases, dct):
for name, value in dct.items():
if name.startswith('test') and inspect.isroutine(value):
dct[name] = decorator(value)
return super().__new__(cls, clsname, bases, dct)
return TestDecoType
class Potato(object, metaclass=make_test_deco_type(test_decorator)):
def test_method(self):
print('in method')
class Spud(Potato):
def test_derived(self):
print('in derived')
在Python 2.x上,您将使用__metaclass__ = make_test_deco_type(test_decorator)
作为类主体的第一行,而不是使用类语句的metaclass=...
部分。您还需要将super()
替换为super(TestDecoType, cls)
。