First, lets take a look at functions & methods and then define attributes for them.
Functions Vs Methods:
In [2]: def f():
...: pass
...:
In [3]: class C:
...: def m(self):
...: pass
...:
We have just defined a function and a class(with a method in it).
In [18]: type(f)
Out[18]: function
In [19]: type(C.m)
Out[19]: function
In [20]: type(C().m)
Out[20]: method
In [21]: set(dir(C().m)) - set(dir(f))
Out[21]: {'__func__', '__self__'}
As seen above, a function binded to an instance of a class is method and an unbound method is a just a function. Also a method has
__self__
and __func__
attributes in addition attributes of a function.Attributes:
Lets add some attributes to function & method and see how they work.
In [21]: setattr(f, 'state', 1)
In [22]: hasattr(f, 'state')
Out[22]: True
In [24]: getattr(f, 'state')
Out[24]: 1
We can do the same thing with unbound method(which is nothing but a function) also
In [31]: setattr(C.m, 'state', 2)
In [32]: hasattr(C.m, 'state')
Out[32]: True
In [33]: getattr(C.m, 'state')
Out[33]: 2
But we cant do the same thing with bound methods.
In [34]: setattr(C().m, 'state', 3)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-34-1379e94f2f48> in <module>()
----> 1 setattr(C().m, 'state', 2)
AttributeError: 'method' object has no attribute 'state'
When users add attributes to a function, they are stored in its
__dict__
attribute.In [40]: f.__dict__
Out[40]: {'state': 1}
In [49]: C.m.__dict__
Out[49]: {'state': 2}
As we have seen in the beginning, method objects just hold reference to its class(
__self__
) and function(__func__
). But they don't have its own __dict__
to hold custom attributes. So we cannot set custom attributes to instance methods.
But we can get the function that is referenced by bound method and set attribute for it.
In [36]: setattr(C().m.__func__, 'state', 3)
In [39]: getattr(C().m.__func__, 'state')
Out[39]: 3
Also methods provide, special
__getattr__
which forwards attribute access to function object. So, this will workIn [52]: hasattr(C().m, 'state')
Out[52]: True
In [53]: getattr(C().m, 'state')
Out[53]: 3
So we can just set attributes to functions & unbound methods just like classes but we can't do it for bound methods.