首先,类A
有两个类变量和两个实例变量:
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
我们可以看到int类的类变量和实例变量都可以正常工作:
In [3]: a = A()
In [4]: a.ivar, a.cvar
Out[4]: (100, 1)
但是,如果检查函数类型变量,情况就会发生变化:
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)
我知道python已将a.cfun(1,2)
翻译为A.cfun(a,1,2)
,然后出错。
我的问题是:由于cvar
和cfun
都是类变量,为什么python会以不同的方式对待它们?
答案 0 :(得分:3)
实际上,分配给类成员的函数仍然有效:
def x():pass
class A:
f = x
e = None
g = None
print(A.__dict__['f'])
# <function x at 0x10e0a6e60>
从实例中检索它时,它会动态转换为方法对象:
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“用户定义的方法”:
当获取类的属性(可能通过该类的实例),如果该属性是用户定义的函数对象,未绑定的用户定义的方法对象或类时,可以创建用户定义的方法对象方法对象...请注意,每次从类或实例检索属性时,都会发生从函数对象到(未绑定或绑定)方法对象的转换。
此转换仅发生在分配给类的函数中,而不发生在实例上。请注意,这已在Python 3中进行了更改,其中Class.fun
返回正常函数,而不是“未绑定方法”。
至于你的问题为什么需要它,一个方法对象本质上是一个包含一个函数及其执行上下文(“self”)的闭包。想象一下,你有一个对象并将其方法用作某个回调。在许多其他语言中,您必须传递对象和方法指针或手动创建闭包。例如,在javascript:
中 myListener = new Listener()
something.onSomeEvent = myListener.listen // won't work!
something.onSomeEvent = function() { myListener.listen() } // works
Python在幕后为我们管理:
myListener = Listener()
something.onSomeEvent = myListener.listen // works
另一方面,有时在课堂上使用“裸”功能或“外国”方法是可行的:
def __init__(..., dir, ..):
self.strip = str.lstrip if dir == 'ltr' else str.rstrip
...
def foo(self, arg):
self.strip(arg)
上述惯例(class vars =&gt; methods,instance vars =&gt; functions)提供了一种方便的方法。
无需添加,就像python中的其他内容一样,可以更改此行为,即编写一个不将其函数转换为方法的类并按原样返回它们。