Automatically decorating every instance method in a class

前端 未结 2 569
耶瑟儿~
耶瑟儿~ 2021-02-05 22:40

I want to apply the same decorator to every method in a given class, other than those that start and end with __.

It seems to me it should be doable using

相关标签:
2条回答
  • 2021-02-05 23:24

    I think this is better done with a metaclass, in order to handle both runtime and subclass method decoration. I don't see an elegant way to handle subclasses automatically with a class decorator.

    from types import FunctionType
    
    # check if an object should be decorated
    def do_decorate(attr, value):
        return ('__' not in attr and
                isinstance(value, FunctionType) and
                getattr(value, 'decorate', True))
    
    # decorate all instance methods (unless excluded) with the same decorator
    def decorate_all(decorator):
        class DecorateAll(type):
            def __new__(cls, name, bases, dct):
                for attr, value in dct.iteritems():
                    if do_decorate(attr, value):
                        dct[attr] = decorator(value)
                return super(DecorateAll, cls).__new__(cls, name, bases, dct)
            def __setattr__(self, attr, value):
                if do_decorate(attr, value):
                    value = decorator(value)
                super(DecorateAll, self).__setattr__(attr, value)
        return DecorateAll
    
    # decorator to exclude methods
    def dont_decorate(f):
        f.decorate = False
        return f
    

    And an example of its use (Python 2, but trivially modified for Python 3):

    def printer(f):
        print f
        return f
    
    class Foo(object):
        __metaclass__ = decorate_all(printer)
        def bar(self):
            pass
        @dont_decorate
        def baz(self):
            pass
        @classmethod
        def test(self):
            pass
    # prints
    # <function bar at 0x04EB59B0>
    
    class AnotherName(Foo):
        def blah(self):
            pass
    # prints
    # <function blah at 0x04EB5930>
    
    Foo.qux = lambda: 1
    # prints
    # <function <lambda> at 0x04EB57F0>
    
    0 讨论(0)
  • 2021-02-05 23:37

    You could do this (not sure if this is the most elegant way though):

    def get_all_instance_methods(x):
        return filter(callable, map(lambda d: getattr(x, d), dir(x)))
    

    As for the cls.method_name, you will have to use getattr(cls, method_name).

    0 讨论(0)
提交回复
热议问题