On 08/01/2014 19:56, axis.of.wea...@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')
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to