如何在类实例方法装饰器上使用类属性?

时间:2014-06-25 19:13:15

标签: python

我有一些看起来像这样的课程:

class Foo:
    bar = None

    @baz(frob=bar)
    def oof():
        pass

class Rab(Foo):
    bar = 'zab'

我无法控制@baz装饰器,我需要使用新类bar的类属性Rab。但是调用Rab.oof()会使用基类的bar=None

3 个答案:

答案 0 :(得分:5)

所以这里有一种奇怪/有趣的方法,你只需要修改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()

我还必须根据self是一种方法的假设向oof添加oof参数,如果baz也将函数转换为静态方法I& #39;我不确定这种方法是否有效。

答案 1 :(得分:3)

可以Foo解包闭包并拉出包装的函数,然后将它重新包装在派生类的装饰器中。但是,如果有任何方法可以让Foo甚至baz更改其实施方式,那就更好了。

class Rab(Foo):
    bar = 'zab'
    oof = baz(frob=bar)(Foo.oof.func_closure[0].cell_contents)

答案 2 :(得分:2)

您可以保留未修饰版本的oof,让我们将其称为_oof_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)