How to pass self into a decorator?

拥有回忆 提交于 2021-02-19 01:37:07

问题


How do I pass self.key below into the decorator?

class CacheMix(object):

    def __init__(self, *args, **kwargs):
        super(CacheMix, self).__init__(*args, **kwargs)

    key_func = Constructor(
        memoize_for_request=True,
        params={'updated_at': self.key}
    )

    @cache_response(key_func=key_func)
    def list(self, *args, **kwargs):
        pass

class ListView(CacheMix, generics.ListCreateAPIView):
    key = 'test_key'

I get the error:

'self' is not defined

回答1:


Here's an example of doing it with a class decorator as I tried to describe to you in the comments. I filled-in a few undefined references in your question and used a super-simplified version of your cache_response function decorator, but hopefully this will convey the idea concretely enough for you to be able adapt it to your real code.

import inspect
import types

class Constructor(object):
    def __init__(self, memoize_for_request=True, params=None):
        self.memoize_for_request = memoize_for_request
        self.params = params
    def __call__(self):
        def key_func():
            print('key_func called with params:')
            for k, v in self.params.items():
                print('  {}: {!r}'.format(k, v))
        key_func()

def cache_response(key_func):
    def decorator(fn):
        def decorated(*args, **kwargs):
            key_func()
            fn(*args, **kwargs)
        return decorated
    return decorator

def example_class_decorator(cls):
    key_func = Constructor(  # define key_func here using cls.key
        memoize_for_request=True,
        params={'updated_at': cls.key} # use decorated class's attribute
    )
    # create and apply cache_response decorator to marked methods
    # (in Python 3 use types.FunctionType instead of types.UnboundMethodType)
    decorator = cache_response(key_func)
    for name, fn in inspect.getmembers(cls):
        if isinstance(fn, types.UnboundMethodType) and hasattr(fn, 'marked'):
            setattr(cls, name, decorator(fn))
    return cls

def decorate_me(fn):
    setattr(fn, 'marked', 1)
    return fn

class CacheMix(object):
    def __init__(self, *args, **kwargs):
        super(CacheMix, self).__init__(*args, **kwargs)

    @decorate_me
    def list(self, *args, **kwargs):
        classname = self.__class__.__name__
        print('list() method of {} object called'.format(classname))

@example_class_decorator
class ListView(CacheMix):
    key = 'test_key'

listview = ListView()
listview.list()

Output:

key_func called with params:
  updated_at: 'test_key'
list() method of ListView object called



回答2:


I just found out that if you write the decorator function like so:

def decorator(the_func):
    @wraps(the_func)
    def wrapper(*args, **kwargs):
        the_func(*args, **kwargs)
    return wrapper

and decorate any method which takes self as an argument, self will appear in args. Therefore you can do this:

from functools import wraps
class myClass:

    def __init__(self):
        self.myValue = "Hello"

    def decorator(the_func):     
        @wraps(the_func)
        def wrapper(*args, **kwargs):
            print(args[0].myValue)
            the_func(*args, **kwargs)
        return wrapper    

    @decorator
    def myFunction(self):
        print("World")

Call it like you normally would

foo = myClass()
foo.myFunction()

and you should get

Hello
World


来源:https://stackoverflow.com/questions/33593846/how-to-pass-self-into-a-decorator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!