Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between class variables of different types?

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?

like image 639
Eastsun Avatar asked Oct 21 '22 11:10

Eastsun


1 Answers

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.

like image 67
georg Avatar answered Oct 28 '22 21:10

georg