Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python decorators that are part of a base class cannot be used to decorate member functions in inherited classes

Python decorators are fun to use, but I appear to have hit a wall due to the way arguments are passed to decorators. Here I have a decorator defined as part of a base class (the decorator will access class members hence it will require the self parameter).

class SubSystem(object):     def UpdateGUI(self, fun): #function decorator         def wrapper(*args):             self.updateGUIField(*args)             return fun(*args)         return wrapper      def updateGUIField(self, name, value):         if name in self.gui:             if type(self.gui[name]) == System.Windows.Controls.CheckBox:                 self.gui[name].IsChecked = value #update checkbox on ui              elif type(self.gui[name]) == System.Windows.Controls.Slider:                 self.gui[name].Value = value # update slider on ui           ... 

I've omitted the rest of the implementation. Now this class is a base class for various SubSystems that will inherit from it - some of the inherited classes will need to use the UpdateGUI decorator.

class DO(SubSystem):     def getport(self, port):         """Returns the value of Digital Output port "port"."""         pass      @SubSystem.UpdateGUI     def setport(self, port, value):         """Sets the value of Digital Output port "port"."""         pass 

Once again I have omitted the function implementations as they are not relevant.

In short the problem is that while I can access the decorator defined in the base class from the inherited class by specifiying it as SubSystem.UpdateGUI, I ultimately get this TypeError when trying to use it:

unbound method UpdateGUI() must be called with SubSystem instance as first argument (got function instance instead)

This is because I have no immediately identifiable way of passing the self parameter to the decorator!

Is there a way to do this? Or have I reached the limits of the current decorator implementation in Python?

like image 597
Aphex Avatar asked Sep 23 '10 19:09

Aphex


People also ask

Can Python decorators be used on classes?

In Python, decorators can be either functions or classes. In both cases, decorating adds functionality to existing functions.

What are decorators in Python used for?

A decorator in Python is a function that takes another function as its argument, and returns yet another function . Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.

Which decorator function is used to create a class method?

A decorator in Python is any callable Python object that is used to modify a function or a class. A reference to a function "func" or a class "C" is passed to a decorator and the decorator returns a modified function or class.

What are the types of decorators in Python?

In fact, there are two types of decorators in Python — class decorators and function decorators — but I will focus on function decorators here.


2 Answers

You need to make UpdateGUI a @classmethod, and make your wrapper aware of self. A working example:

class X(object):     @classmethod     def foo(cls, fun):         def wrapper(self, *args, **kwargs):             self.write(*args, **kwargs)             return fun(self, *args, **kwargs)         return wrapper      def write(self, *args, **kwargs):         print(args, kwargs)  class Y(X):     @X.foo     def bar(self, x):         print("x:", x)  Y().bar(3) # prints: #   (3,) {} #   x: 3 
like image 163
kennytm Avatar answered Sep 18 '22 15:09

kennytm


It might be easier to just pull the decorator out of the SubSytem class: (Note that I'm assuming that the self that calls setport is the same self that you wish to use to call updateGUIField.)

def UpdateGUI(fun): #function decorator     def wrapper(self,*args):         self.updateGUIField(*args)         return fun(self,*args)     return wrapper  class SubSystem(object):     def updateGUIField(self, name, value):         # if name in self.gui:         #     if type(self.gui[name]) == System.Windows.Controls.CheckBox:         #         self.gui[name].IsChecked = value #update checkbox on ui          #     elif type(self.gui[name]) == System.Windows.Controls.Slider:         #         self.gui[name].Value = value # update slider on ui          print(name,value)  class DO(SubSystem):     @UpdateGUI     def setport(self, port, value):         """Sets the value of Digital Output port "port"."""         pass  do=DO() do.setport('p','v') # ('p', 'v') 
like image 32
unutbu Avatar answered Sep 17 '22 15:09

unutbu