Firstly, there is class A
with two class variables and two instance variables:
In [1]: def fun(x, y): return x + y
In [2]: class A:
...: cvar = 1
...: cfun = fun
...: def __init__(self):
...: self.ivar = 100
...: self.ifun = fun
We can see that both class variable and instance variable of int type works fine:
In [3]: a = A()
In [4]: a.ivar, a.cvar
Out[4]: (100, 1)
However, things have changed if we check the function type variables:
In [5]: a.ifun, a.cfun
Out[5]:
(<function __main__.fun>,
<bound method A.fun of <__main__.A instance at 0x25f90e0>>)
In [6]: a.ifun(1,2)
Out[6]: 3
In [7]: a.cfun(1,2)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/future/<ipython-input-7-39aa8db2389e> in <module>()
----> 1 a.cfun(1,2)
TypeError: fun() takes exactly 2 arguments (3 given)
I known that python has translated a.cfun(1,2)
to A.cfun(a,1,2)
and then error raised.
My question is: Since both cvar
and cfun
are class variable, why do python treat them in difference way?
Actually, a function assigned to a class member remains function:
def x():pass
class A:
f = x
e = None
g = None
print(A.__dict__['f'])
# <function x at 0x10e0a6e60>
It's converted on the fly to a method object when you retrieve it from an instance:
print(A().f)
# <bound method A.x of <__main__.A instance at 0x1101ddea8>>
http://docs.python.org/2/reference/datamodel.html#the-standard-type-hierarchy "User-defined methods":
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, an unbound user-defined method object, or a class method object... Note that the transformation from function object to (unbound or bound) method object happens each time the attribute is retrieved from the class or instance.
This conversion only occurs to functions assigned to a class, not to an instance. Note that this has been changed in Python 3, where Class.fun
returns a normal function, not an "unbound method".
As to your question why is this needed, a method object are essentially a closure that contains a function along with its execution context ("self"). Imagine you've got an object and use its method as a callback somewhere. In many other languages you have to pass both object and method pointers or to create a closure manually. For example, in javascript:
myListener = new Listener()
something.onSomeEvent = myListener.listen // won't work!
something.onSomeEvent = function() { myListener.listen() } // works
Python manages that for us behind the scenes:
myListener = Listener()
something.onSomeEvent = myListener.listen // works
On the other side, sometimes it's practical to have "bare" functions or "foreign" methods in a class:
def __init__(..., dir, ..):
self.strip = str.lstrip if dir == 'ltr' else str.rstrip
...
def foo(self, arg):
self.strip(arg)
This above convention (class vars => methods, instance vars => functions) provides a convenient way to have both.
Needless to add, like everything else in python, it's possible to change this behavior, i.e. to write a class that doesn't convert its functions to methods and returns them as is.
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