Can a decorator of an instance method access the class?

后端 未结 13 958
没有蜡笔的小新
没有蜡笔的小新 2020-11-27 12:33

I have something roughly like the following. Basically I need to access the class of an instance method from a decorator used upon the instance method in its definition.

13条回答
  •  醉酒成梦
    2020-11-27 13:00

    As Mark suggests:

    1. Any decorator is called BEFORE class is built, so is unknown to the decorator.
    2. We can tag these methods and make any necessary post-process later.
    3. We have two options for post-processing: automatically at the end of the class definition or somewhere before the application will run. I prefer the 1st option using a base class, but you can follow the 2nd approach as well.

    This code shows how this may works using automatic post-processing:

    def expose(**kw):
        "Note that using **kw you can tag the function with any parameters"
        def wrap(func):
            name = func.func_name
            assert not name.startswith('_'), "Only public methods can be exposed"
    
            meta = func.__meta__ = kw
            meta['exposed'] = True
            return func
    
        return wrap
    
    class Exposable(object):
        "Base class to expose instance methods"
        _exposable_ = None  # Not necessary, just for pylint
    
        class __metaclass__(type):
            def __new__(cls, name, bases, state):
                methods = state['_exposed_'] = dict()
    
                # inherit bases exposed methods
                for base in bases:
                    methods.update(getattr(base, '_exposed_', {}))
    
                for name, member in state.items():
                    meta = getattr(member, '__meta__', None)
                    if meta is not None:
                        print "Found", name, meta
                        methods[name] = member
                return type.__new__(cls, name, bases, state)
    
    class Foo(Exposable):
        @expose(any='parameter will go', inside='__meta__ func attribute')
        def foo(self):
            pass
    
    class Bar(Exposable):
        @expose(hide=True, help='the great bar function')
        def bar(self):
            pass
    
    class Buzz(Bar):
        @expose(hello=False, msg='overriding bar function')
        def bar(self):
            pass
    
    class Fizz(Foo):
        @expose(msg='adding a bar function')
        def bar(self):
            pass
    
    print('-' * 20)
    print("showing exposed methods")
    print("Foo: %s" % Foo._exposed_)
    print("Bar: %s" % Bar._exposed_)
    print("Buzz: %s" % Buzz._exposed_)
    print("Fizz: %s" % Fizz._exposed_)
    
    print('-' * 20)
    print('examine bar functions')
    print("Bar.bar: %s" % Bar.bar.__meta__)
    print("Buzz.bar: %s" % Buzz.bar.__meta__)
    print("Fizz.bar: %s" % Fizz.bar.__meta__)
    

    The output yields:

    Found foo {'inside': '__meta__ func attribute', 'any': 'parameter will go', 'exposed': True}
    Found bar {'hide': True, 'help': 'the great bar function', 'exposed': True}
    Found bar {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
    Found bar {'msg': 'adding a bar function', 'exposed': True}
    --------------------
    showing exposed methods
    Foo: {'foo': }
    Bar: {'bar': }
    Buzz: {'bar': }
    Fizz: {'foo': , 'bar': }
    --------------------
    examine bar functions
    Bar.bar: {'hide': True, 'help': 'the great bar function', 'exposed': True}
    Buzz.bar: {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
    Fizz.bar: {'msg': 'adding a bar function', 'exposed': True}
    

    Note that in this example:

    1. We can annotate any function with any arbitrary parameters.
    2. Each class has its own exposed methods.
    3. We can inherit exposed methods as well.
    4. methods can be overriding as exposing feature is updated.

    Hope this helps

提交回复
热议问题