我想知道你为什么写obj = __import__(t.start.__module__)
我认为它是为主模块保留一个名称,以便有可能通过getattr()函数获取模块名称空间中的对象作为模块的属性。
我通知你,你不必这样做。
globals()是一个字典,表示主空间的全局命名空间。然后可以写,比如globals()["func"]
获取对象func
我还通知您,除了 with 之外,还有另一种方法可以在对象N的名称空间中getattr(N,"xyz")
获取对象xyz ,这要归功于它的名称,它是通过方法:
提供对对象N的名称空间的访问权限xyz
__dict__
N.__dict__
我想知道您的问题是否不在于 Python 的细微之处导致您出错。
在您的问题中,在您写的地方和您写t.__module__
的另一个地方t.start.__module__
。
对于这两种情况,结果是相同的,等于"__main__"
但是,
1) t没有 name 属性__module__
2) t甚至没有 name 属性start
!
如果你打印t.__dict__
,你会看到结果是{ }
!
start并不真正属于实例t的命名空间,因为它实际上属于Test的命名空间
以下代码证明了这些肯定:
class Test:
def __init__(self, *args, **kwargs):
pass
def start(self):
pass
y = Test()
y.ku = 102
print 'y.__dict__',y.__dict__
print 'Test.__dict__',Test.__dict__
结果
y.__dict__ {'ku': 102}
Test.__dict__ {'start': <function start at 0x011E11B0>,
'__module__': '__main__',
'__doc__': None,
'__init__': <function __init__ at 0x011E1170>}
.
这个引用解释了表达式t.__module__
和t.start.__module__
给出答案的原因:
类实例具有作为字典实现的命名空间,这是搜索属性引用的第一个位置。
如果在那里找不到属性,并且实例的类具有该名称的属性,则使用类属性继续搜索。
http://docs.python.org/2/reference/datamodel.html#index-55
因此:
1) t没有 name 的属性,然后 Python 在t__module__
的类中搜索它
2) t没有 name 的属性,然后 Python 在t的类中搜索它
start
.
好吧,所以,Python 到t的类中查找 和 的值t.__module__
上面引用t.start.__module__
所描述的这种机制可以通过以下代码观察到:
class Test:
def __init__(self, *args, **kwargs):
pass
def start(self):
pass
t = Test()
print 'getattr(t,"start")'
print getattr(t,"start")
# result is
getattr(t,"start")
<bound method Test.start of <__main__.Test instance at 0x011DF288>>
我的意思是 Python 回答t的名称为"start"
Test.start 的属性,而不是t.start!
.
因此,从事实来看t.start.__module__
,"__main__"
在我看来,您可能认为 t.start属于模块命名空间,因此您应该能够编写类似的东西getattr(obj,"func")
来获取对象start
但这是错误的。
您在模块的全局命名空间 = 命名空间中找不到方法start(我编写了 METHOD),因为它位于Test的命名空间中。作为一种方法,你必须通过实例或类或通过类本身来获得它。
还有一件事。
我精确:方法,因为我相信方法t.start是基于一个与它不同的功能,它位于模块的命名空间中。实际上,方法是指向实例和此全局命名空间函数的指针的包装器:
如果您仍然不了解方法的工作原理,那么查看实现也许可以澄清问题。当引用不是数据属性的实例属性时,将搜索其类。如果名称表示作为函数对象的有效类属性,则通过将实例对象和刚刚在抽象对象中找到的函数对象打包(指向)来创建方法对象:这就是方法对象。当使用参数列表调用方法对象时,从实例对象和参数列表构造一个新的参数列表,并使用这个新的参数列表调用函数对象。
http://docs.python.org/2/tutorial/classes.html#method-objects
好吧,我想强调的是,似乎某个函数(而不是方法)存在于某处,而方法基于该函数。
我相信这个函数位于模块命名空间中,这就是为什么Test.__dict__["start"]
给出
<function start at 0x011E40F0>
while
getattr(Test,"start")
和getattr(t,"start")
give
<unbound method Test.start>
而<bound method Test.start of <__main__.Test instance at 0x011DF530>>
没有本地化绑定和未绑定方法的原因。
因此,在我看来,就我对文档的正确理解而言(顺便说一句,我发现它并没有很好地解释这些要点)方法是一个包装器,并且基于模块中的一个真实函数namespace,这就是为什么t.start.__module__
和Test.start.__module__
是"__main__"
当我们打印它们时,模块名称空间中存在一个没有出现在模块属性中的函数似乎有点奇怪globals()
:
class Test:
def __init__(self, *args, **kwargs):
pass
def start(self):
pass
def func():
print 'this is a func and not a method!!!'
t = Test()
print '* globals()["func"]'
print globals()["func"]
print id(globals()["func"])
print "===================================="
print '* Test.__dict__["start"]'
print Test.__dict__["start"]
print id(Test.__dict__["start"])
print '----------------------------------------------'
print '* getattr(Test,"start")'
print getattr(Test,"start")
print id(getattr(Test,"start"))
print '----------------------------------------------'
print '* getattr(t,"start")'
print getattr(t,"start")
print id(getattr(t,"start"))
print "===================================="
print globals()
结果
* globals()["func"]
<function func at 0x011C27B0>
18622384
====================================
* Test.__dict__["start"]
<function start at 0x011DEFB0>
18739120
----------------------------------------------
* getattr(Test,"start")
<unbound method Test.start>
18725304
----------------------------------------------
* getattr(t,"start")
<bound method Test.start of <__main__.Test instance at 0x011DF418>>
18725304
{'__builtins__': <module '__builtin__' (built-in)>,
'__package__': None,
't': <__main__.Test instance at 0x011DF418>,
'func': <function func at 0x011C27B0>,
'Test': <class __main__.Test at 0x011DC538>,
'__name__': '__main__',
'__doc__': None}
但事实是,我们确实看到了一个Test.__dict__["start"]
函数,其地址 18739120 与绑定和未绑定方法的地址 18725304 不同。
此外,我看不出还有哪种解释可以解释所暴露的全部事实。
事实上,没有收到任何指定名称的函数不会出现在模块命名空间中,这并不奇怪。
它是一个内部函数,是 Python 所必需的,不是程序员可以使用的,仅此而已。
关于func,使用您使用的所有方法:
class Test:
def __init__(self, *args, **kwargs):
pass
def start(self):
pass
def func():
print 'this is a func and not a method!!!'
t = Test()
print 'func --->',func
print id(func)
print '\nobj = __import__(t.start.__module__) done\n'
obj = __import__(t.start.__module__)
print '* globals()["func"]'
print globals()["func"]
print id(globals()["func"])
print '* obj.__dict__["func"]'
print obj.__dict__["func"]
print "* getattr(obj, 'func')"
print getattr(obj, 'func')
print "* getattr(__import__('__main__'), 'func')"
print getattr(__import__('__main__'), 'func')
结果
func ---> <function func at 0x011C2470>
18621552
obj = __import__(t.start.__module__) done
* globals()["func"]
<function func at 0x011C2470> # <== address in hexadecimal
18621552 # <== address in decimal
* obj.__dict__["func"]
<function func at 0x011C2470>
* getattr(obj, 'func')
<function func at 0x011C2470>
* getattr(__import__('__main__'), 'func')
<function func at 0x011C2470>
.
现在使用以下方法分解您的方法obj
:
class Test:
def __init__(self, *args, **kwargs):
pass
def start(self):
pass
print 'Test.start -->',Test.start
print id(Test.start)
print '----------------------------------------------'
print '* Test.__dict__["start"]'
print Test.__dict__["start"]
print id(Test.__dict__["start"])
print '* getattr(Test,"start")'
print getattr(Test,"start")
print id(getattr(Test,"start"))
print '\n'
print 't.start -->',t.start
print id(t.start)
print '----------------------------------------------'
print '* t.__dict__["start"]'
try:
print t.__dict__["start"]
print id(t.__dict__["start"])
except KeyError as e:
print 'KeyError :',e
print '* getattr(t,"start")'
print getattr(t,"start")
print id(getattr(t,"start"))
结果
Test.start --> <unbound method Test.start>
18725264
----------------------------------------------
* Test.__dict__["start"]
<function start at 0x011E40F0>
18759920
* getattr(Test,"start")
<unbound method Test.start>
18725264
t.start --> <bound method Test.start of <__main__.Test instance at 0x011DB940>>
18725264
----------------------------------------------
* t.__dict__["start"]
KeyError : 'start'
* getattr(t,"start")
<bound method Test.start of <__main__.Test instance at 0x011DB940>>
18725264
直到现在,我很少使用getattr( )
.
我意识到这些结果getattr( )
以特定方式给出结果:属性start被描述为一个方法,根据它是通过实例还是通过类获得的绑定或未绑定。
但是,仅__dict__
提供有关对象真实属性的精确信息。
因此,我们看到start并不真正在实例的命名空间中,而只是在类的命名空间中。
当它作为命名空间的元素时,它被描述为一个函数,而当它Test.__dict__
作为一个属性通过时,它被描述为一个方法getattr( )
。在最后一种情况下没有给出地址。
vars( )函数的使用给出了相同的结果。