Reputation: 3551
I need a parent class with a decorator defined internally that saves all functions in its class to a list, which is an attribute of the parent class. All children of this class must be able to use the decorator, but storing to a list owned by the specific child.
After numerous attempts at defining such a decorator, I am at a loss for how it would be done. Any help would be greatly appreciated! An example of my preferred usage is shown below.
class Parent:
decorated_functions = []
# insert decorator definition
class ChildOne(Parent):
@decorator
def a(self):
return 'a'
@decorator
def b(self):
return 'b'
class ChildTwo(Parent):
@decorator
def c(self):
return 'c'
class ChildThree(Parent):
@decorator
def d(self):
return 'd'
@decorator
def e(self):
return 'e'
@decorator
def f(self):
return 'f'
ChildOne().decorated_functions
# [<function __main__.ChildOne.a>, <function __main__.ChildOne.b>]
ChildTwo().decorated_functions
# [<function __main__.ChildTwo.c>]
ChildThree().decorated_functions
# [<function __main__.ChildThree.d>, <function __main__.ChildThree.e>, <function __main__.ChildThree.f>]
Update #1 Using Brendan Abel's metaclass, I have tried using the following code.
class Child(Parent):
@decorator
def a(self):
return 'a'
@decorator
def b(self):
return 'b'
print(Child().decorated_functions)
However, Child()
does not seem to have an attribute decorated_functions
.
AttributeError: type object 'Child' has no attribute 'decorated_functions'
Update #2 The above code now works with Brendan Abel's solution! The issue was a change in syntax for metaclasses Python 3.
Upvotes: 0
Views: 57
Reputation: 37549
You probably won't be able to do this without turning decorated_functions
into a property (which allows it to be computed after the class has been created), or using a class decorator or metaclasses. I never thought I'd say this, but a metaclass might be the simplest solution here
def decorator(f):
f.decorated = True
return f
class DecoMeta(type):
def __new__(cls, name, bases, attrs):
decorated_functions = []
for v in attrs.values():
if getattr(v, 'decorated', None):
decorated_functions.append(v)
attrs['decorated_functions'] = decorated_functions
return super(DecoMeta, cls).__new__(cls, name, bases, attrs)
class Parent(object):
__metaclass__ = DecoMeta
Edit:
In Python 3, the metaclass hook is slightly different
class Parent(object, metaclass=DecoMeta):
...
Upvotes: 3