由于对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很难理解。
答案 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.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
属性中):< / 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_self
为None
。
当调用绑定方法时,它会调用带有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 2和Python 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)
如果从类或实例调用函数,则调用其__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
将作为第一个参数隐式传递。
答案 5 :(得分:0)
函数,未绑定方法和绑定方法有什么区别?
从突破性的什么是功能角度看,没有区别。 Python面向对象的功能建立在基于函数的环境上。
受约束等于:
这里是示例:
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.m1
和ci.m2
都被绑定; ci.m1
是因为我们发送了指向实例的指针self
,而ci.m2
是因为实例知道该类已绑定;)。
请注意,方法最初可能不是该类的一部分。查看Alex Martelli的this答案以了解更多详细信息。