【问题标题】:Python decorator class on namespaces with inheritance具有继承性的命名空间上的 Python 装饰器类
【发布时间】:2014-12-28 09:32:48
【问题描述】:

尝试在 python 3 的基类上使用装饰器类,但不完全理解我观察到的行为。

class tagged:
    """ Decorator itself. """
    def __init__(self, regClass, *args, **kwargs):
        """ called after class definition """
        print(self, regClass, regClass.name)

@tagged
class Base(object):
    name = "Base class"

#class Derived(Base):
#    name = "Derived class"

第一堂课按预期工作,我明白了

__init__ <__main__.tagged object at 0x100632cc0> <class '__main__.Base'>
Base class

但是当我取消注释 Derived 时,它并没有按照我预期的方式将其参数转发给装饰器。

_init__ <__main__.tagged object at 0xb74c516c> <class '__main__.Base'>
Base class
__init__ <__main__.tagged object at 0xb74c51cc> Derived
Traceback (most recent call last):
  File "./prog.py", line 10, in <module>
  File "./prog.py", line 4, in __init__
AttributeError: 'str' object has no attribute 'name'

我在这里的动机是改进我的 Pyfu,特别是我正在尝试在子模块中实现自我注册的各种方法(具体来说,这些模块是使用子命令索引注册自己的子命令,然后提供如果且仅在选择特定子命令时,则将其参数设置为解析器)。

【问题讨论】:

  • 我在下面给出了解释,但是还有很多关于你为什么要这样做的原因尚不清楚。一方面,你为什么要让你的装饰器成为一个类而不是一个函数?
  • @BrenBarn 我花了很多时间阅读有关装饰器的内容,并且“使用类”似乎是鼓励这样做的方式,以及其他 S/O 问题 (stackoverflow.com/questions/7492068/…) ..

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


【解决方案1】:

这里有一些问题。主要的是你的tagged 装饰器,当用于装饰一个类时,返回一个tagged 的实例,而不是一个类。这就是装饰器的工作方式:您的类 Base 定义被调用 tagged(A) 的结果替换。装饰器不仅仅是“在类定义之后调用”——装饰器调用的结果替换原始类。所以你的装饰类最终根本不是一个类,而是一个实例(tagged)。

因此,Derived 的元类最终成为tagged(因为这是它的基“类”Base 的类型,如前所述,它实际上是tagged 的一个实例),所以定义Derived 尝试调用 tagged.__init__ 并失败,因为它为 regClass 传递的参数是类的名称(“派生”)而不是类。 (我从您的问题中猜测,您为什么会收到此特定错误的详细信息可能与您尝试做的事情无关。)

目前还不清楚您要在这里做什么。您提到“将其参数转发给装饰器”,但类装饰器不是这样工作的。当你装饰一个类时,它也不会自动装饰它的子类。

【讨论】:

  • FWIW,超出了问题的范围:我正在尝试各种方法来实现命名空间的自我注册(不是类实例,而是类定义)。基本上我有一个带有定义“Base”的 init.py 的模块目录,然后要向系统添加一个新命令,您可以创建具有“类 NewCommand(Base)”和“ NewCommand”会在解析器中自动显示,但只有在您启动顶级引擎并使用“NewCommand”命令时才会实际实例化。
  • @kfsone:如果您希望类自动注册自己,请查看元类。
  • 我见过的大多数元类机制都需要某种形式的样板(每个派生都必须选择加入),我希望装饰器被实现为某种形式的继承属性,所以我可以通过一个装饰来实现这一点,但我会回到那条大道,ty :)
  • @kfsone:我认为你倒退了。如果你装饰一个类,它的任何子类都不会被装饰,除非你也手动装饰它们。但是如果一个类有一个特定的元类,那么它的所有子类也都有那个元类,除非你手动给它们一个不同的。因此,通常装饰器是“选择加入”而元类是“选择退出”。 SO上有很多关于装饰器和元类的问题需要查看。然后,如果您仍有疑问,可以继续提出另一个问题。
【解决方案2】:

为了放大 BrenBarn 在他的回答中所说的话,我修改了你的代码。 “tagged”现在是一个返回类的函数。如您所见,此代码运行没有错误(已使用 python 3.3 确认)。输出表明名称“Base”现在绑定到类对象,因此可以由 Derived 继承。但是字符串“派生类”从不打印。

我不确定这是否可以用来完成您想要的,但它可能有助于阐明类装饰器的工作方式。

def tagged(regClass):
    """ Decorator itself. """
    class tagme:   
        def __init__(self):
            """ called after class definition """
            print(self)
            print(regClass)
            print(regClass.name)
    return tagme

@tagged
class Base(object):
    name = "Base class"

print("After defining base",Base)
b = Base()

class Derived(Base):
    name = "Derived class"

d = Derived()

【讨论】:

    猜你喜欢
    • 2014-07-29
    • 2011-03-23
    • 2020-06-03
    • 2018-02-09
    • 2014-12-13
    • 2015-07-18
    • 2017-12-10
    • 2011-03-01
    • 2019-12-07
    相关资源
    最近更新 更多