简体   繁体   中英

Inheritable Tracking Decorator

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.

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):
    ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM