在下面的代码中,类A有一个成员函数,它被设置为指向在类外定义的函数。 在类B中,相同的函数设置为类定义中的外部指针。 为类型A的对象调用函数将失败,因为self不会传递给函数。但是对于B来说,自我通过了。 为什么自我被传递给B,而不是A?
def f1(s,a):
print s
print a
class A(object):
def __init__(self):
self.fp1 = f1
class B(object):
fp1 = f1
a=A()
b=B()
try:a.fp1("blaa")
except Exception, e: print `e`
try:b.fp1("bluu")
except Exception, e: print `e`
输出:
TypeError('f1() takes exactly 2 arguments (1 given)',)
<__main__.B object at 0x2ab0dacabed0>
bluu
答案 0 :(得分:4)
当您执行self.fp1 = f1
时,您刚刚为类A
的实例变量分配了一个函数。因此,当你调用它时,你必须传递两个参数。
当你这样做时:
class B(object):
fp1 = f1
在类B
的创建过程中,python在类范围内找到了一个函数fp1
,并从中创建了一个instancemethod
(用名为fp1
的变量替换了instancemethod
从它之前保存的函数创建的)。当您在对象instancemethod
上调用self
时,会自动将其作为第一个参数传递。
您可以输入以下命令来检查:
>>> a = A()
>>> b = B()
>>> type(a.fp1)
function
>>> type(b.fp1)
instancemethod
答案 1 :(得分:4)
在类A
中,您将函数绑定到实例。这可以被视为“函数指针”,因此必须显式传递所有参数。在类B
中,将函数绑定到类,这将使函数作为方法工作。您可以将类定义A
修改为
class A(object):
def __init__(self):
A.fp1 = f1
会给出相同的行为{class 1}},即所有实例中的B
指向fp1
,或者您可以包裹f1
。
f1
这样可以单独更改每个实例的class A(object):
def __init__(self):
self.fp1 = lambda a: f1(self, a)
。后一种变体可能就是你想要的。
答案 2 :(得分:1)
使instance.method(...)
等同于Class.method(instance, ...)
的魔力取决于函数对象是该类的属性。细节有所不同(以及它们,您可以创建这种方法的丑陋的解决方法)。在Python 3中,所有函数都是descriptors。在Python 2中,有一些特殊的 unbound方法对象,这些对象是隐式创建的,用于包装存储为类属性的函数,并大致完成所有函数在Python 3中自行完成的任务。
在任何一种情况下,通过实例访问它都会创建一个绑定方法,它在调用时将实例作为第一个参数传递。在任何一种情况下,通过实例属性访问的函数都不是特殊的,它只是另一个可以传递和使用的对象。
您可以使用partial
(一点漏洞抽象)来实现类似的行为:
from functools import partial
# in __init__
self.fp1 = partial(f1, self)
或通过创建委托方法:
def __init__(self):
self._fp1 = f1
def fp1(*args, **kwds):
return self._fp1(self, *args, **kwds)
答案 3 :(得分:0)
在第一种情况下,您在类中创建一个字段,该字段中存储有方法对象。所以“a.fp1”不是方法调用,因此“a”不作为第一个参数。它是方法对象的检索,然后调用它。
对于第二种情况,您可以参考documentation:
作为类属性的任何函数对象都定义了一个方法 该类的实例。
因此,对于b,“fp1”成为b类实例的方法。
您可以在此处找到更详细的说明:method objects vs function objects , Python class instances vs class