类__init__中的Python函数指针

时间:2013-08-15 22:41:00

标签: python function pointers init

在下面的代码中,类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

4 个答案:

答案 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