Reputation: 2581
I was wondering if it possible to use a descriptor's decorator within a subclass.
class Descriptor():
def __get__(self, instance_obj, objtype):
raise Exception('ouch.')
def decorate(self, f):
print('decorate', f)
return f
class A():
my_attr = Descriptor()
class B():
@my_attr.decorate
def foo(self):
print('hey, whatsup?')
# --> NameError: name 'my_attr' is not defined
This, of course, does not work since my_attr
is undefined within the class definition of B
.
Next I tried:
class B():
@A.my_attr.decorate
def foo(self):
print('hey, whatsup?')
# --> Exception: ouch.
But, this approach invokes the descriptor __get__
method (where the instance_obj
argument is None
) and therefore the test Exception is fired. To access the decorator one could check for the instance_obj
to be None
an return the descriptor itself:
def __get__(self, instance_obj, objtype):
if instance_obj is None:
return self
raise Exception('avoid this')
# --> decorate <function B.foo at 0x1021dd7b8>
It works! But is it plausible or is there a way to use the decorator within the class definition of B
?
Upvotes: 5
Views: 939
Reputation: 1124858
You can bypass the descriptor protocol altogether by retrieving the original object from the __dict__
mapping of the class:
A.__dict__['my_attr'].decorate
or cleaner, using vars()
:
vars(A)['my_attr'].decorate
However, the @
decorator syntax doesn't allow for subscriptions (you are given only simpler expressions with attribute access and a single call at the end), so you'd have to extract the dictionary first:
_A_my_attr = vars(A)['my_attr']
@_A_my_attr.decorate
def foo(self):
# ...
However, unless you must capture the binding to a class, it is better to guard for the first argument to __get__
being None
, as you discovered. This is exactly what property
objects or functions do.
Upvotes: 4