【问题标题】:Tracking decorated methods of children classes in python在python中跟踪子类的装饰方法
【发布时间】:2021-09-28 05:01:58
【问题描述】:

在 python 中,我如何设置一个父类来为每个孩子单独跟踪具有特定装饰器的方法?我正在尝试做的快速代码sn-p:

class Parent:
    decorated_func_dict = {} #dictionary that stores name->func for decorated functions
    def get_func_by_decorator_name(self, name):
        #stuff
        pass

class Child1(Parent):
    @func_name("Bob")
    def bob_func(self, *args):
        pass

    @func_name("Tom")
    def func2(self, *args):
        pass

class Child2(Parent):
    @func_name("Bob")
    def func_bob2(self, *args):
        pass

foo = Child1()
bar = Child2()

foo.get_func_by_decorator_name("Bob")
#Returns foo.bob_func

bar.get_func_by_decorator_name("Bob")
#Returns bar.func_bob2

使用 Python 3.9。

【问题讨论】:

    标签: python python-3.x python-decorators


    【解决方案1】:

    装饰器不是让函数看起来漂亮的东西。它是一个可调用对象(不仅是函数),执行一些任意操作,并返回一个替换对象。

    在这种情况下,您的装饰器应该在某处的字典中存储对函数对象的引用。问题是,在创建函数之前,您将无法引用定义函数的类,这在装饰器运行之后发生。您可以通过存储类名和函数名来避免这种情况。

    这里的最后一步是将函数对象正确绑定到正确对象上的方法。这是get_func_by_decorated_name 可以为您做的事情。

    总之,你可以这样写:

    decorated_func_dict = {}
    
    def func_name(cls_name, func_name):
        def decorator(func):
            decorated_func_dict.setdefault(cls_name, {})[func_name] = func
            return func
        return decorator
    
    class Parent:
        def get_func_by_decorator_name(self, name):
            return decorated_func_dict[type(self).__name__][name].__get__(self)
    
    class Child1(Parent):
        @func_name("Child1", "Bob")
        def bob_func(self, *args):
            pass
    
        @func_name("Child1", "Tom")
        def func2(self, *args):
            pass
    
    class Child2(Parent):
        @func_name("Child2", "Bob")
        def func_bob2(self, *args):
            pass
    

    你确实得到了:

    >>> foo.get_func_by_decorator_name("Bob")
    <bound method Child1.bob_func of <__main__.Child1 object at 0x000001D58181E070>>
    >>> bar.get_func_by_decorator_name("Bob")
    <bound method Child2.func_bob2 of <__main__.Child2 object at 0x000001D582041F10>>
    

    另一种方法是给你的函数一个名称属性,然后你可以在__init_subclass__Parent 中聚合到一个映射中。这使您可以使界面更接近您最初的意图:

    def func_name(func_name):
        def decorator(func):
            func.special_name = func_name
            return func
        return decorator
    
    class Parent:
        def __init_subclass__(cls):
            cls.decorated_func_dict = {}
            for item in cls.__dict__.values():
                if hasattr(item, 'special_name'):
                    cls.decorated_func_dict[item.special_name] = item
                    del item.special_name  # optional
        def get_func_by_decorator_name(self, name):
            return self.decorated_func_dict[name].__get__(self)
    
    class Child1(Parent):
        @func_name("Bob")
        def bob_func(self, *args):
            pass
    
        @func_name("Tom")
        def func2(self, *args):
            pass
    
    class Child2(Parent):
        @func_name("Bob")
        def func_bob2(self, *args):
            pass
    

    结果与第一个示例相同。

    最简单的方法当然是在创建类之前访问孩子的命名空间,例如使用元类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-23
      • 2021-07-18
      • 2012-02-09
      • 2019-03-15
      • 2019-11-27
      • 1970-01-01
      • 2018-07-14
      • 2022-01-16
      相关资源
      最近更新 更多