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

时间:2012-08-14 09:53:23

标签: python oop function methods

由于对this answer的评论主题的讨论,我问这个问题。我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描述?如何调用这三个对象?他们怎么能相互转化?关于这些内容的documentation很难理解。

6 个答案:

答案 0 :(得分:60)

功能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())

答案 1 :(得分:7)

  

很难理解

嗯,这是一个非常难的话题,它与描述符有关。

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

>>> 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)

现在,方法。方法是一些香料的功能。描述符在这里进入游戏。如Data Model中所述,A.f1A().f1分别被翻译为A.__dict__['f1'].__get__(None, A)type(a).__dict__['f1'].__get__(a, type(a))。这些__get__的结果与原始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

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

注意,im_self属性为None

>>> f.im_self is None
True

如果bound method,此逻辑会立即向原始f1提供其创建的A实例(此实例实际存储在im_self属性中):< / p>

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

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

答案 2 :(得分:3)

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

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

当调用绑定方法时,它会调用带有im_func im_self的{​​{1}}作为跟随其调用参数的第一个参数。 unbound方法仅使用其调用参数调用底层函数。

答案 3 :(得分:2)

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

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")

答案 4 :(得分:2)

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

我的解释如下。

班级Function摘要:

Python 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)

Python 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.xB.__dict__['x'].__get__(None, B)相同。 在Python 3中,这将返回普通函数。 在Python 2中,这将返回一个未绑定的函数。

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

答案 5 :(得分:0)

  

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

从突破性的什么是功能角度看,没有区别。 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永远不会被称为 bound C.m2绑定到C类,因为我们发送了cls参数,它是类指针。

ci.m1ci.m2都被绑定; ci.m1是因为我们发送了指向实例的指针self,而ci.m2是因为实例知道该类已绑定;)。

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


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