Python装饰器:如何在子类中使用父类装饰器

时间:2011-09-19 15:10:42

标签: python decorator

注意

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

此问题的接受答案显示将装饰器移动到模块范围。


编辑:使用上一个例子是一个坏主意。希望这更清楚:

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:名称'deco'未在B内定义。 装饰器需要在类范围内,因为我需要访问存储状态。

第三编辑:关于斯文的建议,我试过这个:

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)。有什么指针吗?

2 个答案:

答案 0 :(得分:5)

问题在于继承适用于实例属性查找,而不适用于类定义。所以当你尝试用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()

答案 1 :(得分: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的重点。只需将其移动到模块命名空间即可。