Understanding decorator and class methods

Rotwang sg552 at hotmail.co.uk
Wed Jan 8 18:17:22 EST 2014


On 08/01/2014 19:56, axis.of.weasel at gmail.com wrote:
> can someone please explain why the following works, in contrast to the second example?
>
> def decorator(func):
>      def on_call(*args):
>          print args
>          return func(args)
>      return on_call
>
> class Foo:
>      @decorator
>      def bar(self, param1):
>          print 'inside bar'
>
> f=Foo()
> f.bar(4)  # from where is the decorator getting the Foo instance?
>
>
>
> I understand why the following works/does not work
>
> class decorator2:
>      def __init__(self, func):
>          self.func=func
>      def __call__(self, *args):
>          self.func(*args)
>
> class Foo2:
>      @decorator2
>      def bar2(self, param): pass
>
>
> f2 = Foo2()
> Foo2.bar2(f2, 4) # works, Foo2 instance and param are passed to decorator2 call
> f2.bar2(4) # does not work, Foo2 instance is missing, decorator2 cannot invoke method bar

 From http://docs.python.org/3/reference/datamodel.html:

Instance methods

     An instance method object combines a class, a class instance and
     any callable object (normally a user-defined function).

     [...]

     User-defined method objects may be created when getting an
     attribute of a class (perhaps via an instance of that class), if
     that attribute is a user-defined function object or a class method
     object.

     [...]

     Note that the transformation from function object to instance
     method object happens each time the attribute is retrieved from the
     instance. In some cases, a fruitful optimization is to assign the
     attribute to a local variable and call that local variable. Also
     notice that this transformation only happens for user-defined
     functions; other callable objects (and all non-callable objects)
     are retrieved without transformation.


Notice the last sentence in particular. After being decorated by 
decorator2 Foo2.bar2 is not a user-defined function (i.e. an instance of 
types.FunctionType), so is not transformed into a method upon being 
accessed through an instance. I suppose you could create a class that 
mimics the behaviour of methods, though I don't know why you would want 
to. The following is tested with 3.3.0; I expect someone who knows more 
than I will probably be along soon to point out why it's stupid.

class decorator3:
     def __init__(self, func):
         self.func = func
     def __call__(self, *args, **kwargs):
         print('Calling func(self, *%r, **%r)' % (args, kwargs))
         return self.func(self.__self__, *args, **kwargs)
     def __get__(self, instance, owner):
         self.__self__ = instance
         return self

class Foo3:
     @decorator3
     def bar3(self, param):
         return self, param

 >>> f3 = Foo3()
 >>> f3.bar3('param')
Calling func(self, *('param',), **{})
(<__main__.Foo3 object at 0x0000000002BDF198>, 'param')



More information about the Python-list mailing list