71

我之所以问这个问题是因为对此答案的评论线程进行了讨论。我已经完成了 90% 的工作。

In [1]: class A(object):  # class named 'A'
   ...:     def f1(self): pass
   ...:
In [2]: a = A()  # an instance

f1以三种不同的形式存在:

In [3]: a.f1  # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1  # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1']  # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1']  # a function
Out[6]: <function __main__.f1>

绑定方法未绑定方法函数对象有什么区别,都是f1描述的?如何称呼这三个对象?它们如何相互转化?关于这些东西的文档很难理解。

4

6 回答 6

82

函数def语句创建,或由lambda. 在 Python 2 中,当函数出现在class语句的主体中(或传递给type类构造调用)时,它会被转换为未绑定的方法。(Python 3 没有未绑定的方法;见下文。)在类实例上访问函数时,它会转换为绑定的方法,该方法会自动将实例作为第一个self参数提供给该方法。

def f1(self):
    pass

f1是一个函数

class C(object):
    f1 = f1

现在C.f1是一个未绑定的方法。

>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True

我们还可以使用type类构造函数:

>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>

我们可以f1手动转换为未绑定的方法:

>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>

未绑定的方法通过对类实例的访问进行绑定:

>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>

访问通过描述符协议转换为调用:

>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

结合这些:

>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>

或直接:

>>> types.MethodType(f1, C(), C)                
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

函数和未绑定方法的主要区别在于后者知道它绑定到哪个类;调用或绑定未绑定的方法需要其类类型的实例:

>>> f1(None)
>>> C.f1(None)
TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead)
>>> class D(object): pass
>>> f1.__get__(D(), D)
<bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>>
>>> C.f1.__get__(D(), D)
<unbound method C.f1>

由于函数和未绑定方法之间的区别非常小,Python 3摆脱了这种区别;在 Python 3 下,访问类实例上的函数只会为您提供函数本身:

>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True

那么,在 Python 2 和 Python 3 中,这三个是等价的:

f1(C())
C.f1(C())
C().f1()

将函数绑定到实例具有将其第一个参数(通常称为self)固定到实例的效果。因此,绑定方法C().f1等效于以下任一方法:

(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())
于 2012-08-14T10:09:53.380 回答
9

很难理解

嗯,这是一个相当困难的话题,它与描述符有关。

让我们从功能开始。这里一切都很清楚 - 你只需调用它,所有提供的参数都会在执行时传递:

>>> f = A.__dict__['f1']
>>> f(1)
1

TypeError如果参数数量有任何问题,则会引发常规:

>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)

现在,方法。方法是带有一点香料的函数。描述符在这里出现。如数据模型中所述,A.f1A().f1分别翻译为A.__dict__['f1'].__get__(None, A)type(a).__dict__['f1'].__get__(a, type(a))。这些__get__'s 的结果与原始f1函数不同。这些对象是原始对象的包装器,f1并包含一些额外的逻辑。

如果unbound method这种逻辑包括检查第一个参数是否是 的实例A

>>> f = A.f1
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead)
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead) 

如果此检查成功,它将f1以该实例作为第一个参数执行 original:

>>> f(A())
<__main__.A object at 0x800f238d0>

请注意,该im_self属性是None

>>> f.im_self is None
True

如果bound method此逻辑立即提供原始f1实例,A则创建它的实例(此实例实际上存储在im_self属性中):

>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>

因此,bound意味着底层函数绑定到某个实例。unbound意味着它仍然是绑定的,但仅限于一个类。

于 2012-08-14T10:25:37.870 回答
4

函数对象是由函数定义创建的可调用对象。绑定和未绑定方法都是由点二元运算符调用的描述符创建的可调用对象。

绑定和未绑定的方法对象有3个主要属性:im_func是类中定义的函数对象,im_class是类,im_self是类实例。对于未绑定的方法,im_selfNone.

当一个绑定的方法被调用时,它调用im_funcwithim_self作为第一个参数,然后是它的调用参数。未绑定的方法仅使用其调用参数调用底层函数。

从 Python 3 开始,没有未绑定的方法。Class.method返回对该方法的直接引用。

于 2012-08-14T10:49:30.363 回答
2

有关详细信息,请参阅Python 2Python 3文档。

我的解释如下。

Function片段:

蟒蛇 3:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        if obj is None:
            return self
        return types.MethodType(self, obj)

蟒蛇2:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        return types.MethodType(self, obj, objtype)
  1. 如果一个函数在没有类或实例的情况下被调用,它就是一个普通函数。
  2. 如果从类或实例调用函数,__get__则调用它来检索包装函数
    B.x与 相同B.__dict__['x'].__get__(None, B)。在 Python 3 中,这将返回普通函数。在 Python 2 中,这将返回一个未绑定的函数。

    湾。b.x与 相同type(b).__dict__['x'].__get__(b, type(b)。这将在 Python 2 和 Python 3 中返回一个绑定方法,这意味着self将作为第一个参数隐式传递。

于 2018-05-22T08:00:09.430 回答
2

函数、未绑定方法和绑定方法有什么区别?

从开创性的角度来看,什么是功能没有区别。Python 面向对象的特性建立在基于函数的环境之上。

被绑定等于:

该函数是否将cls)或对象实例self)作为第一个参数?

这是示例:

class C:

    #instance method 
    def m1(self, x):
        print(f"Excellent m1 self {self} {x}")

    @classmethod
    def m2(cls, x):
        print(f"Excellent m2 cls {cls} {x}")

    @staticmethod
    def m3(x):
        print(f"Excellent m3 static {x}")    

ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)

print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)

输出:

Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>

输出显示静态函数m3永远不会被调用boundC.m2绑定到C类,因为我们发送的cls参数是类指针。

ci.m1并且ci.m2都受到约束;ci.m1因为我们发送self了指向实例的指针,并且ci.m2因为实例知道类已绑定;)。

总而言之,您可以根据方法采用的第一个参数将方法绑定到类或类对象。如果方法未绑定,则可以将其称为未绑定。


请注意,方法最初可能不是该类的一部分。查看Alex Martelli 的这个答案以获取更多详细信息。

于 2019-05-24T14:49:47.463 回答
1

我今天看到的一件有趣的事情是,当我将一个函数分配给一个类成员时,它就变成了一个未绑定的方法。如:

class Test(object):
    @classmethod
    def initialize_class(cls):
        def print_string(self, str):
            print(str)
        # Here if I do print(print_string), I see a function
        cls.print_proc = print_string
        # Here if I do print(cls.print_proc), I see an unbound method; so if I
        # get a Test object o, I can call o.print_proc("Hello")
于 2014-11-15T06:45:27.837 回答