I am having trouble understanding why the following happens. I am having a decorator which does nothing except it checks whether a function is a method. I thought I have understood what method in Python is, but obviously, this is not the case:
import inspect
def deco(f):
def g(*args):
print inspect.ismethod(f)
return f(*args)
return g
class Adder:
@deco
def __call__(self, a):
return a + 1
class Adder2:
def __call__(self, a):
return a + 2
Adder2.__call__ = deco(Adder2.__call__)
Now, running the following:
>>> a = Adder()
>>> a(1)
False
2
>>> a2 = Adder2()
>>> a2(1)
True
3
I would expect for this code to print True two times.
So, decorating the function manually as in the Adder2 is not an exact equivalent to decorating via the @deco function?
Can someone be so glad and explain why this happens?
To decorate a method in a class, first use the '@' symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function. Here, when we decorate, multiply_together with integer_check, the integer function gets called.
Decorator Method is a Structural Design Pattern which allows you to dynamically attach new behaviors to objects without changing their implementation by placing these objects inside the wrapper objects that contains the behaviors.
In Python, decorators can be either functions or classes. In both cases, decorating adds functionality to existing functions. When we decorate a function with a class, that function becomes an instance of the class. We can add functionality to the function by defining methods in the decorating class.
A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.
Methods are functions that are associated with a class. Methods are only created when you retrieve them from an already defined class; a method is a wrapper around a function, with a reference to the class as well (and optionally a reference to the instance).
What happens in the first case is: Python compiles your class definition Adder
. It finds the decorator definition and a function. The decorator is passed the function, returning a new function. That function is added to the class definition (stored in the class __dict__
). All this time you are dealing with a python function, not a method. That happens later.
When you then call a(1)
, a lookup reveals that the instance doesn't have a __call__
but the Adder
class does, so it is retrieved using __getattribute__()
. This finds a function (your deco
decorator), which is a descriptor so it's __get__()
method is called (so Adder.__call__.__get__(a, Adder))
, returning a bound method, which is then called and passed in the 1
value. The method is bound because instance
is not None when __get__()
is called. Your decorator, which wrapped a function at class building time, prints False
because it was passed an unwrapped function to start with.
In the second case, however, you retrieve a method (again via __getattribute__()
calling __get__()
on the undecorated Adder2.__call__
function), this time unbound (as there is no instance, only a class passed to __get__()
(the full call is Adder2.__call__.__get__(None, Adder2)
), and you then decorate that method. Now ismethod()
prints True.
Note that in Python 3, the latter case changes. In Python 3 there no longer is a concept of an unbound method, only functions and bound methods. The term 'bound' is thus dropped altogether. Your second case would also print False
as Adder2.__call__.__get__(None, Adder2)
returns a function in that case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With