Ben*_*son 57 python oop methods function
我问这个问题是因为对这个答案的评论主题进行了讨论.我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描述?如何调用这三个对象?他们怎么能相互转化?关于这些东西的文档很难理解.
eca*_*mur 68
一个功能是通过创建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())
很难理解
嗯,这是一个相当难的话题,它与描述符有关.
让我们从功能开始.这里一切都很清楚 - 你只需要调用它,所有提供的参数在执行时传递:
>>> 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.f1并A().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属性中):
>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>
因此,bound意味着底层函数绑定到某个实例.unbound意味着它仍然是绑定的,但仅限于一个类.