【问题标题】:get all functions marked by decorator of a class获取由类的装饰器标记的所有函数
【发布时间】:2021-02-18 13:21:35
【问题描述】:

我正在尝试存储在类中定义的特定 actions

为了减少代码重复,我想使用一个 mixin 类来存储基于装饰器的所有操作。

这个想法是其他人应该可以直接使用新操作来扩展类。我特别想避免在源代码中明确列出这些操作(这应该由装饰器处理)。

这就是我想出的。不幸的是,在所有.actions 列表中,所有类的所有操作都被列出。

但是,我想要一个只列出特定类的操作的解决方案。

class ActionMixin:
    actions = []

    @staticmethod
    def action(fun):
        ActionMixin.actions.append(fun)
        return fun


class Human(ActionMixin):
    @ActionMixin.action
    def talk(self):
        pass


class Dog(ActionMixin):
    @ActionMixin.action
    def wuff(self):
        pass


class Cat(ActionMixin):
    @ActionMixin.action
    def miau(self):
        pass


if __name__ == "__main__":
    party = [Human(), Dog()]
    possible_actions = [action for memer in party for action in member.actions]
    # I would like that possible_actions is now only Human.talk() and Dog.wuff()
    # instead it is 2 times all actions
    print(len(possible_actions))  # == 6

【问题讨论】:

  • 必须是mixin吗?
  • 不一定,但我认为这是最简单的。如果您有一个更简单的解决方案,那么无需重复代码即可工作,这对我来说很好
  • 你能说明你想做什么吗? HumanDog 均见 完全相同 ActionMixin.actions。通过迭代for memer in party,您明确要求将所有内容列出两次。
  • @MisterMiyagi 那是我的问题——我不希望他们使用完全相同的 ActionMixin.actions

标签: python decorator mixins


【解决方案1】:

我会在这里写我自己的描述符。所以:

class Registry:
    def __init__(self):
        self._registered = []
    def __call__(self, func):
        self._registered.append(func)
        return func
    def __get__(self, obj, objtype=None):
        return self._registered


class Human:
    actions = Registry()
    @actions
    def talk(self):
        pass


class Dog:
    actions = Registry()
    @actions
    def wuff(self):
        pass


class Cat:
    actions = Registry()
    @actions
    def miau(self):
        pass

因此,不要从 mixin 继承,只需初始化描述符对象即可。然后该对象本身可以用作装饰器(__call__ 方法!)。

请注意,装饰器可以是您为其分配的任何名称,它是存储操作的属性的名称。

在 REPL 中:

In [11]: party = [Human(), Dog()]

In [12]: [action for member in party for action in member.actions]
Out[12]: [<function __main__.Human.talk(self)>, <function __main__.Dog.wuff(self)>]

编辑:

如果您希望它存在于基类中,则必须更改实现。基本上,使用dict 来跟踪注册表,不幸的是,我们不得不依靠脆弱的__qualname__ 来获取__call__ 中的课程:

class ActionsRegistry:
    def __init__(self):
        self._registry = {}
    def __call__(self, func):
        klass_name, func_name = func.__qualname__.rsplit('.', 1)
        if klass_name not in self._registry:
            self._registry[klass_name] = []
        self._registry[klass_name].append(func)
        return func
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return self._registry[objtype.__qualname__]

class Base:
    actions = ActionsRegistry()


class Human(Base):
    @Base.actions
    def talk(self):
        pass


class Dog(Base):
    @Base.actions
    def wuff(self):
        pass


class Cat(Base):
    @Base.actions
    def miau(self):
        pass

【讨论】:

  • 这是一个很好的解决方案,我会接受。还有一个后续问题:在我的应用程序中,所有类(Human、Dog、Cat)都继承自 Base 类。是否可以只在该基类中分配actions 并在子类中使用@actions 装饰器?
  • @user7431005 确保return func 来自__call__,至于这个最新的要求,我想也许我可以破解一些东西,它可能不漂亮
  • @user7431005 好的,一起破解了一些东西,有一点小问题 - 它只能检索实例上的实际操作列表,而不是类
  • 就像一个魅力 - 我会在我们的项目中试一试,看看它的长期表现如何;-)
猜你喜欢
  • 2020-12-18
  • 2011-12-02
  • 2017-09-16
  • 2021-10-31
  • 2011-03-28
  • 2011-06-06
  • 1970-01-01
  • 2021-06-24
相关资源
最近更新 更多