2

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

4 回答 4

4

当你这样做时,self.fp1 = f1你只是将一个函数分配给了 class 的一个实例变量A。因此,当您调用它时,您必须传递两个参数。

当你这样做时:

   class B(object):
       fp1 = f1

在类的创建过程中,Bpythonfp1在类范围内找到了一个函数并从中创建了一个函数instancemethod(将变量名称替换为fp1instancemethod它之前保存的函数中创建的)。当你调用一个instancemethod对象时,它self会自动作为第一个参数传递。

您可以通过键入以下内容进行检查:

>>> a = A()
>>> b = B()
>>> type(a.fp1)
function
>>> type(b.fp1)
instancemethod
于 2013-08-15T22:55:21.300 回答
4

在类A中,您将函数绑定到实例。这实际上可以被视为“函数指针”,因此必须显式传递所有参数。在类B中,您将函数绑定到将导致函数作为方法工作的类。您可以将类定义修改A

class A(object):
    def __init__(self):
        A.fp1 = f1

这将给出相同的行为有类B,即fp1所有实例指向f1,或者你可以换行f1

class A(object):
    def __init__(self):
        self.fp1 = lambda a: f1(self, a)

这将允许单独更改fp1每个实例。后一种变体可能是您正在寻找的。

于 2013-08-15T22:57:06.583 回答
1

instance.method(...)等同于的魔法Class.method(instance, ...)取决于作为类属性的函数对象。细节各不相同(以及它们,您可以创建这种方法的丑陋解决方法)。在 Python 3 中,所有函数都是描述符。在 Python 2 中,有一些特殊的未绑定方法对象被隐式创建,以包装存储为类属性的函数,并大致完成 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)
于 2013-08-15T22:56:53.480 回答
0

在第一种情况下,您在类中创建一个字段,其中存储了一个方法对象。所以“a.fp1”不是一个方法调用,因此“a”没有作为第一个参数。这是一个方法对象的检索,然后调用它。

对于第二种情况,您可以参考文档

任何作为类属性的函数对象都为该类的实例定义了一个方法。

因此,对于 b,“fp1”成为类 b 实例的方法。

你可以在这里找到更详细的解释:方法对象 vs 函数对象,Python 类实例 vs 类

于 2013-08-15T22:54:30.267 回答