Python 装饰器:如何在子类中使用父类装饰器
注意:
另一个问题的接受答案展示了如何使用父装饰器。
这个问题的接受答案展示了如何将装饰器移动到模块范围。
编辑:使用之前的例子真是个坏主意。希望这个更清楚:
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:在B里面找不到'deco'这个名字。装饰器需要在类的范围内,因为我需要访问存储的状态。
第三次编辑:根据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()需要一个参数(但给了0个)。有什么建议吗?
2 个回答
3
对编辑前问题的回答: 你的问题现在和之前链接的那个问题完全一样。比起链接问题里的答案,解决这个问题的一个更简单的方法就是把装饰器移出类的命名空间。
另外,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
没什么意义。直接把它移到模块的命名空间里就好了。
11
问题在于,继承只适用于实例属性的查找,而不适用于类的定义。所以当你在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()