我有一个具有基本方法的类,以及具有相同基本功能但可以使用装饰器实现的其他行为的子类。
class cls_with_basic_method:
#if...exec("@decoratorA")
#if...exec("@decoratorB")
#...
def basic_method(arg):
#...
return arg
class cls_with_basic_method_and_decoratorA(class_with_basic_method):
#...
class cls_with_basic_method_and_decoratorB(class_with_basic_method):
#...
#...
似乎最快的解决方案是,如果我能够在调用子类方法时执行特定的装饰器,但却无法想到在python中表达它的方法。这可以轻松完成吗?
答案 0 :(得分:1)
装饰函数或方法通常是与它装饰的函数或方法不同的对象[*] - 所以,你可以用一种明确的方式包装原始类的方法。这是相当直接的,而且相当无聊 - 但是如果你只需要修饰子类的几个方法它就会起作用:
class cls_with_basic_method:
def basic_method(arg):
#...
return arg
class cls_with_basic_method_and_decoratorA(class_with_basic_method):
basic_method = decoratorA(cls_with_basic_method.basic_method)
class cls_with_basic_method_and_decoratorB(class_with_basic_method):
basic_method = decoratorB(cls_with_basic_method.basic_method)
唯一特别的事情是使用带有常规函数调用语法的装饰器而不是使用“@ ...”语法 - 这样它们就可以在表达式中使用。
这种方法更加无聊,因为你必须在每个装饰中对类体内的超类名称进行硬编码,因为你不能从类体中使用super
,只能从内部方法中使用。{/ p>
[*]虽然一些装饰器只是将元数据添加到它们可装饰的可调用对象并返回对象本身 - 这种方法对于这样的装饰器不起作用,因为它们也会影响超类中的方法。
现在,进一步解决您的问题 - 您想要的只是在子类上调用超类时的任意方法。如果覆盖类__getattribute__
,那么可以或多或少地自动完成 - 然后你可以创建一个具有特殊“装饰器”属性的类层次结构,该属性将为每个方法调用调用 - 或多或少像这样:
class cls_with_basic_method:
_auto_decorate = set(("basic_method", ...))
_decorator = lambda x: x # NOP decorator
def basic_method(arg):
#...
return arg
def __getattribute__(self, attrname):
attr = object.__getattribute__(self, attr)
# shortcircuit non-method retrievelas as fast as possible:
if not attrname in __class__._auto_decorate not callable(attr):
return attr
return self.__class__._decorator(attr)
class cls_with_basic_method_and_decoratorA(class_with_basic_method):
_decorator = decoratorA
class cls_with_basic_method_and_decoratorB(class_with_basic_method):
_decorator = decoratorB
当然,如果您需要针对不同方法的不同装饰器,只需相应地更改__getattribute__
中的代码 - 最简单的方法是使_decorator
属性成为字典,而不是指向简单功能。
(旁注:__class__
魔术变量,当在方法中使用时,是Python 3的东西:它自动包含对其定义的类的引用(在这种情况下,{{1 }})。
这种方法将在每次调用时重新调整方法 - 它的开销并不像看起来那么多 - Python的默认方法检索机制本身也同样复杂 - 但是如果你喜欢在类创建时修饰方法,那么tehn你可以在元类中使用类似的机制,而不是依赖cls_with_basic_method
。
__getattribute__
这实际上比它看起来更简单:在层次结构上创建一个新类时,它只搜索所有具有from itertools import chain
class AutoDecorate(type):
def __new__(metacls, name, bases, dct):
if "_decorator" not in dct:
dct["_decorator"] = lambda x: x # NOP decorator
all_bases = list(chain(base.__mro__ for base in bases))
for base in all_bases:
if not "_auto_decorate" in base.__dict__:
continue
for method_name in base.auto_decorate:
if method_name not in dct:
dct[method_name] = dct["_decorator"](getattr(base, method_name))
return super().__new__(name, bases, dct)
class cls_with_basic_method(metaclass=AutoDecorate):
_auto_decorate = set(("basic_method", ...))
def basic_method(arg):
#...
return arg
class cls_with_basic_method_and_decoratorA(class_with_basic_method):
_decorator = decoratorA
class cls_with_basic_method_and_decoratorB(class_with_basic_method):
_decorator = decoratorB
属性的超类 - 然后它获取该列表中的方法,然后进行装饰它们与正在创建的类的_auto_decorate
属性中的装饰器一起使用。
根据您的要求,我会说您正在处理一个需要“aspect oriented programing”方法的项目。有几个Python库可以提供该功能 - 也许你应该看看它。如果您这么认为,请搜索可以提供适当的Python面向方面功能的模块并使用它们。