【问题标题】:Python decorators: how to use parent class decorators in a child class [duplicate]Python装饰器:如何在子类中使用父类装饰器[重复]
【发布时间】:2011-11-20 08:19:57
【问题描述】:

注意

另一个问题的接受答案显示了如何使用父装饰器。

这个问题的公认答案显示将装饰器移动到模块范围。


编辑:使用前面的例子是个坏主意。希望这更清楚:

class A:
    def deco( func ):
        print repr(func)
        def wrapper( self, *args ):
            val = func( *args )
            self.do_something()
            return val
        return wrapper

    def do_something( self ):
        # Do something
        print 'A: Doing something generic for decoration'

    @deco
    def do_some_A_thing ( self ):
        # Do something 
        print 'A: Doing something generic'

class B ( A ):

    @deco
    def do_some_B_thing( self ):
        # Do something
        print "B: Doing something specific"

a = A()
b = B()
a.do_some_A_thing()
b.do_some_B_thing()

#Expected Output:
    #A: Doing something generic
    #A: Doing something generic for decoration
    #B: Doing something specific
    #A: Doing something generic for decoration

此代码生成 NameError: name 'deco' is not defined inside B. 装饰器需要在类范围内,因为我需要访问存储的状态。

第三次编辑:根据 Sven 的建议,我尝试了这个:

class A:
    def deco( func ):
        def wrapper( self, *args ):
            val = func( *args )
            self.do_something(*args)
            return val
        return wrapper

    def do_something( self ):
        # Do something
        print 'A: Doing something generic for decoration'

    @deco
    def do_some_A_thing ( self ):
        # Do something 
        print 'A: Doing something generic'

    deco = staticmethod(deco)

class B ( A ):

    @A.deco
    def do_some_B_thing( self ):
        # Do something
        print "B: Doing something specific"



a = A()
b = B()
a.do_some_A_thing()
b.do_some_B_thing()

#Expected Output:
    #A: Doing something generic
    #A: Doing something generic for decoration
    #B: Doing something specific
    #A: Doing something generic for decoration

我现在有 TypeError: do_some_A_thing() 只需要 1 个参数(给定 0)。有什么指点吗?

【问题讨论】:

  • 您是否尝试过这样做?你收到错误了吗?

标签: python decorator


【解决方案1】:

问题在于继承适用于实例属性查找,而不适用于类定义。所以当你尝试在 B 中用 A.deco 进行装饰时,它找不到它。解决方案是将deco 移出模块范围,因为self 这个名称没有什么神奇之处,您可以继续使用它。您还需要将self 显式传递给func,并且您确实不需要需要将它传递给self.do_something()。这是更新的代码:

def deco( func ):
    print repr( func )
    def wrapper( self, *args ):
        val = func( self, *args )
        self.do_something()
        return val
    return wrapper

class A:
    def do_something( self ):
        # Do something
        print 'A: Doing something generic for decoration'

    @deco
    def do_some_A_thing ( self ):
        # Do something 
        print 'A: Doing something generic'

class B ( A ):

    @deco
    def do_some_B_thing( self ):
        # Do something
        print "B: Doing something specific"

a = A()
b = B()
a.do_some_A_thing()
b.do_some_B_thing()

【讨论】:

  • 此解决方案仅在子类与子类在同一模块内定义时才有效。如果我在其他地方导入父类并尝试定义子类,在子类的方法之一上使用装饰器,则不再定义它。
  • @user5359531:什么不再定义?
  • 我的意思是如果你从另一个模块导入父类,装饰器就不再定义了
  • @user5359531:如果装饰器与父模块在同一个模块中定义,您应该能够同时导入两者——还是我遗漏了什么?
  • 是的,我的意思是您现在需要同时导入两者。如果你有很多装饰器,这意味着你现在需要手动为它们都包含一个导入语句。否则,您必须使用@modulename.decorator 给他们打电话。
【解决方案2】:

对已编辑问题的回答:您的问题与question it linked to before the edit 完全相同。比链接问题的答案更简单的解决方法是简单地将装饰器移出类命名空间。

此外,self.do_something(self) 应该改为 self.do_something() -- 不要通过 self 两次。

编辑前的问题回答:如果您只想用装饰器装饰实例方法,请不要将 self 参数折叠到 *args 中,而是让它明确:

def _deco(func):
    def wrapper(self, *args):
        res = func(self, *args)
        self.some_other_baseclass_method(*args)
        return res
    return wrapper

也就是说,我没有看到在类命名空间中包含 _deco 并将其转换为 staticmethod 的意义。只需将其移至模块命名空间即可。

【讨论】:

  • 谢谢斯文。我的问题措辞很糟糕。我已对其进行了更新以更好地反映问题。
  • @GeneralBecos:更新了我的答案。
  • 我试过这种方法,它会导致 TypeError: do_some_A_thing() 只需要 1 个参数(给定 0)。
  • 啊,我才意识到你也是对的。我以为你的意思是 _deco 留在 A 班。谢谢!
猜你喜欢
  • 2019-09-01
  • 2012-02-09
  • 2011-10-04
  • 2021-07-16
  • 2020-11-25
  • 1970-01-01
  • 2011-04-28
  • 2014-01-14
  • 2011-03-26
相关资源
最近更新 更多