Inside a decorator-class, access instance of the class which contains the decorated method

浪尽此生 提交于 2020-01-01 02:45:10

问题


I have the following decorator, which saves a configuration file after a method decorated with @saveconfig is called:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *args):
        self.f(object, *args)
        # Here i want to access "cfg" defined in pbtools
        print "Saving configuration"

I'm using this decorator inside the following class. After the method createkvm is called, the configuration object self.cfg should be saved inside the decorator:

class pbtools()
    def __init__(self):
        self.configfile = open("pbt.properties", 'r+')
        # This variable should be available inside my decorator
        self.cfg = ConfigObj(infile = self.configfile)

    @saveconfig
    def createkvm(self):
        print "creating kvm"

My problem is that i need to access the object variable self.cfg inside the decorator saveconfig. A first naive approach was to add a parameter to the decorator which holds the object, like @saveconfig(self), but this doesn't work.

How can I access object variables of the method host inside the decorator? Do i have to define the decorator inside the same class to get access?


回答1:


You have to make your decorator class behave as a descriptor to be able to access the instance:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        def wrapper(*args):
            print "Saving configuration"
            print instance.cfg
            return self.f(instance, *args)
        return wrapper

Your code passes object as first parameter to self.f(), where it should pass the pbtools instance.




回答2:


You are passing object as self to the decorated method. The thing is, you can't easily get self because Python sees the decorated method, which is now an objects, and doesn't consider it a method (that should be passed self when called - or, more generally, that should work as a property returning a bound method). You can work around this, as pointed out by @Sven Marnach.

However, you could easily rewrite this decorator without a class, using a closure (is a bit shorter and also solves the above problem):

def saveconfig(f):
    @functools.wraps(f) # to preserve name, docstring, etc.
    def wrapper(*args, **kwargs): # **kwargs for compability with functions that use them
        f(*args, **kwargs)
        # save config
    return wrapper

Other notes:

  • Terminology mixup: There is no class variable in the example. A class variable would be x = ... indented as far as the method definitions and be shared between all instances (specifically, it would be an attribute of the object that is pbtools) - everything on self is an instance attribute.
  • At class definition time (when you define methods, apply decorators, etc.) there is no self!



回答3:


You can also use a simple function for what you want:

def saveconfig(f):
    # this method replaces the decorated, so `self` will be the pbtools instance
    def wrapped(self, *args):
        f(self, *args)
        # Here i want to access "cfg" defined in pbtools
        print "Saving configuration", self.cfg
    return wrapped

If saveconfig must be a class then you need Sven's solution.



来源:https://stackoverflow.com/questions/4987471/inside-a-decorator-class-access-instance-of-the-class-which-contains-the-decora

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