如果您仍然不了解方法的工作原理,请查看
实施也许可以澄清问题。当一个实例属性
被引用的不是数据属性,它的类被搜索。如果
该名称表示一个有效的类属性,它是一个函数对象,一个
方法对象是通过打包(指向)实例对象来创建的
和刚刚在一个抽象对象中一起找到的函数对象:
这是方法对象。当方法对象被调用时
参数列表,从实例构造一个新的参数列表
对象和参数列表,函数对象被调用
这个新的参数列表。
http://docs.python.org/2/tutorial/classes.html#method-objects
仔细阅读这段摘录。
意思是:
1) 一个实例并不真正拥有一个对象,它是一个将成为其属性的方法。
实际上,实例的__dict__中根本没有“方法”属性(__dict__是对象的命名空间)
2) 当一个“方法”属性被调用时,一个实例似乎有一个“方法”这一事实是由于一个进程,而不是一个实例的命名空间内的方法对象的存在
3) 此外,在类的命名空间中实际上并不存在方法对象。
但是实例是有区别的,因为当这样的调用完成时,一定有什么东西会导致一个真正的方法对象,不是吗?
所谓的类的“方法”属性,为了表述方便,实际上是一个函数对象,是类命名空间中的属性。
也就是说,一个pair (identifier of the function, function)是一个类的__dict__的成员,这个属性允许解释器在一个类的时候构造一个方法对象。方法调用被执行。
4) 同样,当一个“方法”属性被调用时,一个类似乎有一个“方法”这一事实是由于一个进程,而不是一个类的命名空间内的方法对象的存在
编辑我不太确定;见最后
5) 方法对象(不是“方法”对象;我的意思是真正的对象是真正的方法`,摘录中描述的内容)是在调用时创建的,它以前不存在。
它是一种包装器:它将指向实例对象和方法所基于的函数对象的指针打包。
所以,方法是基于函数的。这个函数对我来说是持有所说“方法”的类的真正属性,因为这个函数确实属于该类的命名空间(__dict__):当__dict__ 是时,这个函数被描述为<function ......>印刷。
可以使用别名im_func 或__func__ 从方法对象访问此函数(请参见下面的代码)
.
我相信这些概念不是很广为人知和理解的。但是下面的代码证明了我所说的。
class A(object):
def __init__(self,b=0):
self.b = b
print 'The __init__ object :\n',__init__
def addu(self):
self.b = self.b + 10
print '\nThe addu object :\n',addu
print '\nThe A.__dict__ items :\n',
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in A.__dict__.items())
a1 = A(101)
a2 = A(2002)
print '\nThe a1.__dict__ items:'
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in a1.__dict__.items())
print '\nThe a2.__dict__ items:'
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in a2.__dict__.items())
print '\nA.addu.__func__ :',A.addu.__func__
print id(A.addu.__func__),'==',hex(id(A.addu.__func__))
print
print 'A.addu :\n ',
print A.addu,'\n ',id(A.addu),'==',hex(id(A.addu))
print 'a1.addu :\n ',
print a1.addu,'\n ',id(a1.addu),'==',hex(id(a1.addu))
print 'a2.addu :\n ',
print a2.addu,'\n ',id(a2.addu),'==',hex(id(a2.addu))
a2.addu()
print '\na2.b ==',a2.b
print '\nThe A.__dict__ items :\n',
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in A.__dict__.items())
结果
The __init__ object :
<function __init__ at 0x011E54B0>
The addu object :
<function addu at 0x011E54F0>
The A.__dict__ items :
__module__ : __main__
addu : <function addu at 0x011E54F0>
__dict__ : <attribute '__dict__' of 'A' objects>
__weakref__ : <attribute '__weakref__' of 'A' objects>
__doc__ : None
__init__ : <function __init__ at 0x011E54B0>
The a1.__dict__ items:
b : 101
The a2.__dict__ items:
b : 2002
A.addu.__func__ : <function addu at 0x011E54F0>
18765040 == 0x11e54f0
A.addu :
<unbound method A.addu>
18668040 == 0x11cda08
a1.addu :
<bound method A.addu of <__main__.A object at 0x00CAA850>>
18668040 == 0x11cda08
a2.addu :
<bound method A.addu of <__main__.A object at 0x011E2B90>>
18668040 == 0x11cda08
a2.b == 2012
The A.__dict__ items :
__module__ : __main__
addu : <function addu at 0x011E54F0>
__dict__ : <attribute '__dict__' of 'A' objects>
__weakref__ : <attribute '__weakref__' of 'A' objects>
__doc__ : None
__init__ : <function __init__ at 0x011E54B0>
.
编辑
有些事情困扰着我,我不知道这个主题的深层内容:
以上代码表明A.addu、a1.addu和a2.addu都是同一个方法对象,具有唯一标识。
然而,A.addu 被称为未绑定方法,因为它没有任何关于特定实例的信息,
和a1.addu 和a2.addu 被称为绑定方法,因为每个方法都有指定实例的信息,该实例必须与方法的操作相关。
从逻辑上讲,对我来说,这意味着这三种情况的方法应该不同。
但是这三个的身份相同,而且这个身份与该方法所基于的函数的身份不同。
它得出的结论是,该方法实际上是一个存在于内存中的对象,并且它不会从一个实例的一次调用更改为另一个实例的另一个调用。
然而,打印类的命名空间__dict__,即使在创建实例和调用“方法”addu()之后,这个命名空间也不会暴露一个新对象可以识别为不同于addu 函数的方法对象。
这是什么意思?
它给我的印象是,方法对象一被创建,就不会被销毁,而是存在于内存 (RAM) 中。
但它隐藏起来,只有构成解释器功能的过程才知道如何以及在哪里找到它。
这个隐藏的对象,真正的方法对象,必须能够更改对必须应用函数的实例的引用,或者如果它作为未绑定方法被调用,则引用None。在我看来就是这样,但这只是头脑风暴的假设。
有人知道这次审讯的事情吗?
要回答这个问题,调用 .upper 和 .lower functions 被认为是正确的,因为实际上它们是基于函数作为类的每个方法的。
但是,下面的结果很特殊,可能是因为它们是内置的方法/函数,而不是我的代码中的用户方法/函数。
x = 'hello'
print x.upper.__func__
结果
print x.upper.__func__
AttributeError: 'builtin_function_or_method' object has no attribute '__func__'