函数,未绑定方法和绑定方法有什么区别?
我正在问这个问题,因为这个答案的评论线程的讨论。 我是90%的方法来让我的头。
In [1]: class A(object): # class named 'A' ...: def f1(self): pass ...: In [2]: a = A() # an instance
f1
以三种不同的forms存在:
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>
bound方法 , unbound方法和函数对象之间有什么区别,所有这些都由f1来描述? 如何称呼这三个对象? 他们怎么能相互转化? 关于这个东西的文档是很难理解的。
一个函数是由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
我们也可以使用类的构造函数:
>>> 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>>
函数和非绑定方法的主要区别在于后者知道绑定哪个类; 调用或绑定一个未绑定的方法需要一个类types的实例:
>>> 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())
很难理解
那么,这是一个相当困难的话题,它与描述符有关。
让我们从函数开始。 在这里一切都很清楚 – 你只需要调用它,所有提供的参数在执行时都会被传递:
>>> f = A.__dict__['f1'] >>> f(1) 1
在参数数量有问题的情况下会引发Regular TypeError
:
>>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f1() takes exactly 1 argument (0 given)
现在,方法。 方法是一些香料的function。 描述符进来游戏在这里。 如数据模型中所述 , A.f1
和A().f1
被翻译成A.__dict__['f1'].__get__(None, A)
and 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
提供一个它创build的实例(这个实例实际存储在im_self
属性中):
>>> f = A().f1 >>> f.im_self <__main__.A object at 0x800f23950> >>> f() <__main__.A object at 0x800f23950>
所以, bound
意味着底层函数绑定到某个实例。 unbound
意味着它仍然是有约束力的,但只限于一个阶级。
函数对象是由函数定义创build的可调用对象。 绑定和未绑定的方法都是可调用的对象,由dot二元运算符调用的描述符创build。
绑定和未绑定的方法对象有三个主要属性: im_func
是在类中定义的函数对象, im_class
是类, im_self
是类实例。 对于未绑定的方法, im_self
是None
。
当一个绑定方法被调用时,它调用im_self
作为第一个参数跟在它的调用参数。 未绑定方法只调用其调用参数的底层函数。
我今天看到的一个有趣的事情是,当我给一个类成员分配一个函数时,它变成了一个未绑定的方法。 如:
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")