动态分配 sub class 依赖装饰器
Dynamically assigning sub class dependent decorators
我有一个 class 有一个基本方法,还有一个子 class 有相同的基本功能,但有额外的行为,可以用装饰器实现。
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):
#...
#...
似乎最快的解决方案是,如果我能够在调用 subclass 方法时执行特定的装饰器,但想不出在 python 中表达它的方式.这可以轻松完成吗?
修饰的函数或方法通常是不同于它修饰的函数或方法的对象 [*] - 因此,您可以直接以显式方式包装原始的 class' 方法。这相当简单,而且相当无聊 - 但如果您只需要装饰子 classes:
的几个方法,它就会起作用
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)
唯一特别的地方是使用具有常规函数调用语法的装饰器,而不是使用“@...”语法 - 这样它们就可以在表达式中使用。
这种方法更无聊,因为你必须在每次修饰时在 class 主体中硬编码 superclass 名称,因为你不能使用 super
class 正文,仅来自内部方法。
[*] 虽然一些装饰器只是将元数据添加到它们装饰的可调用对象和 return 对象本身 - 这种方法不适用于此类装饰器,因为它们会影响 superclass 还有。
现在,进一步解决您的问题 - 您想要的只是在 superclass 上调用 subclass 上的任意方法时将它们包装起来。如果您重写 class__getattribute__
,这可以或多或少地自动完成 - 然后您可以创建一个 class 层次结构,其中包含一个特殊的 "decorator" 属性,每个方法都会调用该属性调用 - 或多或少是这样的:
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 事物:它自动包含对 class 的引用定义于(在本例中,cls_with_basic_method
)。
这种方法会在每次调用时重新装饰方法 - 它的开销并不像看起来那么多 - Python 的默认方法检索机制本身也同样复杂 - 但如果你更喜欢装饰class 创建的方法相反,您可以在 metaclass 中使用类似的机制,而不是依赖 __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
这实际上比看起来更简单:在层次结构上创建新的 class 后,它只搜索所有超级 class 具有 _auto_decorate
属性的元素 -然后它获取该列表中的方法,并用正在创建的 class 的 _decorator
属性中的装饰器装饰它们。
根据您的要求,我会说您正在处理一个需要“aspect oriented programing”方法的项目。有几个 Python 库可以提供该功能 - 也许您应该看一看。如果您这么认为,请搜索可以提供适当 Python 面向方面功能的模块并使用它们。
我有一个 class 有一个基本方法,还有一个子 class 有相同的基本功能,但有额外的行为,可以用装饰器实现。
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):
#...
#...
似乎最快的解决方案是,如果我能够在调用 subclass 方法时执行特定的装饰器,但想不出在 python 中表达它的方式.这可以轻松完成吗?
修饰的函数或方法通常是不同于它修饰的函数或方法的对象 [*] - 因此,您可以直接以显式方式包装原始的 class' 方法。这相当简单,而且相当无聊 - 但如果您只需要装饰子 classes:
的几个方法,它就会起作用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)
唯一特别的地方是使用具有常规函数调用语法的装饰器,而不是使用“@...”语法 - 这样它们就可以在表达式中使用。
这种方法更无聊,因为你必须在每次修饰时在 class 主体中硬编码 superclass 名称,因为你不能使用 super
class 正文,仅来自内部方法。
[*] 虽然一些装饰器只是将元数据添加到它们装饰的可调用对象和 return 对象本身 - 这种方法不适用于此类装饰器,因为它们会影响 superclass 还有。
现在,进一步解决您的问题 - 您想要的只是在 superclass 上调用 subclass 上的任意方法时将它们包装起来。如果您重写 class__getattribute__
,这可以或多或少地自动完成 - 然后您可以创建一个 class 层次结构,其中包含一个特殊的 "decorator" 属性,每个方法都会调用该属性调用 - 或多或少是这样的:
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 事物:它自动包含对 class 的引用定义于(在本例中,cls_with_basic_method
)。
这种方法会在每次调用时重新装饰方法 - 它的开销并不像看起来那么多 - Python 的默认方法检索机制本身也同样复杂 - 但如果你更喜欢装饰class 创建的方法相反,您可以在 metaclass 中使用类似的机制,而不是依赖 __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
这实际上比看起来更简单:在层次结构上创建新的 class 后,它只搜索所有超级 class 具有 _auto_decorate
属性的元素 -然后它获取该列表中的方法,并用正在创建的 class 的 _decorator
属性中的装饰器装饰它们。
根据您的要求,我会说您正在处理一个需要“aspect oriented programing”方法的项目。有几个 Python 库可以提供该功能 - 也许您应该看一看。如果您这么认为,请搜索可以提供适当 Python 面向方面功能的模块并使用它们。