Python 如何在类实例方法装饰器上使用类属性?
我有一些类似这样的课程:Python 如何在类实例方法装饰器上使用类属性?,python,Python,我有一些类似这样的课程: class Foo: bar = None @baz(frob=bar) def oof(): pass class Rab(Foo): bar = 'zab' 我无法控制@baz装饰器,我需要使用新类Rab的class属性bar。但是调用Rab.oof()会使用基类的bar=None。您可以从Foo中打开闭包并取出包装的函数,然后在派生类的decorator中重新包装它。但是,如果有任何方法可以通过Foo甚至baz
class Foo:
bar = None
@baz(frob=bar)
def oof():
pass
class Rab(Foo):
bar = 'zab'
我无法控制@baz
装饰器,我需要使用新类Rab
的class属性bar
。但是调用Rab.oof()
会使用基类的bar=None
。您可以从Foo
中打开闭包并取出包装的函数,然后在派生类的decorator中重新包装它。但是,如果有任何方法可以通过Foo
甚至baz
改变它们的实现,那就更好了
class Rab(Foo):
bar = 'zab'
oof = baz(frob=bar)(Foo.oof.func_closure[0].cell_contents)
您可以保留未修饰版本的
oof
,我们称之为\u oof\u raw
,并将其重新包装到子类定义中。为了保留未修饰的版本,您需要“手动”修饰,即不使用@
语法糖
class Foo:
bar = None
def _oof_raw(self):
pass
oof = baz(frob=bar)(_oof_raw)
class Rab(Foo):
bar = 'zab'
oof = baz(frob=bar)(Foo._oof_raw)
这里有一种奇怪的/有趣的方法,您只需要修改
Foo
,这里的想法是创建一个新的装饰器,延迟baz
装饰,直到函数第一次被基于类名的缓存调用,这样它只发生一次
注意,这还包括一个用于baz
的虚拟实现,它只打印提供的frob
参数,但是这种方法应该可以很好地工作,而不需要修改baz
:
def baz(frob):
def deco(func):
def wrapped(*args, **kwargs):
print('frob:', frob)
return func(*args, **kwargs)
return wrapped
return deco
def newdeco(func):
def wrapped(self, *args, **kwargs):
if not hasattr(wrapped, 'cache'):
wrapped.cache = {}
cls = self.__class__.__name__
if cls not in wrapped.cache:
wrapped.cache[cls] = baz(frob=getattr(self.__class__, 'bar'))(func)
wrapped.cache[cls](self, *args, **kwargs)
return wrapped
class Foo:
bar = None
@newdeco
def oof(self):
pass
class Rab(Foo):
bar = 'zab'
f = Foo()
f.oof()
r = Rab()
r.oof()
基于
oof
是一种方法的假设,我还必须在oof
中添加一个self
参数,如果baz
也在将函数转换为静态方法,我不确定这种方法是否有效。问题是@baz(frob=bar)
在Foo
的定义下进行评估,而不是重新评估子类化。你能在Rab
中重载oof
吗?嗯,那肯定行,但我不想那样做。我确实可以控制Foo
。您将如何更改该实现以使其更好?您甚至可以将其与元类混合,在元类\uuuuu new\uuuuu
中进行包装,而不是显式地在每个子类中进行包装。但这仍然是有限的,因为它无法在运行时响应对Rab.bar
的更改,而不与类变量的访问器进行一些非常重要的磨合。@SilasRay,是的,我同意如果这必须应用于许多子类,那么使用元类可能是一种很好的方法。(但如果不是,我个人的偏好是避免不必要的魔法)这是相当整洁的。您可能希望将包装。缓存
作为一个weakkeydirectionary
,并按类对象缓存,而不仅仅是名称(名称可能冲突)。非常好。在这个问题上,这几乎可以肯定是最好的想法这是一个很好的答案,我打算使用檐口资源@view
装饰器在金字塔应用程序上使用它。我不确定它是否适用于此,因为当应用程序启动时,视图会在Pyramid的配置中注册,但我会尝试并让您知道。谢谢如果它最终不起作用,我就会想到,您可能可以通过Foo
上的元类做一些聪明的事情来应用decorator。