How do you use a class attribute to decorate a class instance method?

I have several classes that look something like this:

class Foo: bar = None @baz(frob=bar) def oof(): pass class Rab(Foo): bar = 'zab' 

I have no control over the @baz decorator, and I need to use the class bar attribute of the new Rab class. But calling Rab.oof() uses bar=None base class.

+8
python
source share
3 answers

So here is some weird / interesting approach when you only need to change Foo , the idea here is to create a new decorator that delays baz decoration until the function is first called with some reason to cache on the name class so that it happens only once.

Note that this also includes a dummy implementation for baz that simply prints the frob argument that was provided, but this approach should work fine, without having to modify 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() 

I also had to add the self argument to oof based on the assumption that oof is a method, if baz also converts the function to a static method, I'm not sure if this approach will work.

+5
source share

You can expand the closure with Foo and pull out the wrapped function, and then wrap it again in the decorator in the derived class. But if in any way you can go to Foo or even baz to change their implementation, it would be better.

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

You can save an unconsolidated version of oof , call it _oof_raw and _oof_raw it in a subclass definition. To save the unconsolidated version, you need to decorate "manually", that is, do not use syntactic sugar @ .

 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) 
+2
source share

All Articles