3

说,我有以下被class称为Testmethodstart

>>> class Test:
...     def __init__(self, *args, **kwargs):
...         pass
...     def start(self):
...         pass
... 

现在,我有一个独立的function名为func

>>> def func():
...     print 'this is a func and not a method!!!'
... 
>>> 

[1] 现在,t.start是属于method的一个instance of __main__.Test0xb769678c

>>> t = Test()
>>> t.start
<bound method Test.start of <__main__.Test instance at 0xb769678c>>
>>> 

[2] funcfunction属于哪个位置的0xb767ec6c

>>> func
<function func at 0xb767ec6c>
>>> 

现在,我们可以使用 builtin来提取__module__fromt.start和。不出意外,和属于同一个iefunc__module__funct.startmodule__main__

>>> func.__module__
'__main__'
>>> t.__module__
'__main__'
>>> 

[3] 现在,让我们将__module__for存储t.start在一个变量中obj

>>> obj = __import__(t.start.__module__)
>>> obj
<module '__main__' (built-in)>
>>> 

现在,我getattr()用来获取函数的func句柄如下,输出是[2]<function func at 0xb767ec6c>funcgetattr()identical

>>> print getattr(obj, 'func')
<function func at 0xb767ec6c>
>>> 
>>> print getattr(__import__('__main__'), 'func')
<function func at 0xb767ec6c>
>>> 

问题:

我如何使用和模块名称 [3] 来获取应该是 [1]getattr()的句柄Test.start<bound method Test.start of <__main__.Test instance at 0xb769678c>>

当我尝试使用getattr()时,'t.start'我得到以下Traceback

>>> print getattr(obj, 'Test.start')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'Test.start'
>>> 
>>> 
>>> print getattr(__import__('__main__'), 'Test.start')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'Test.start'
>>> 

换句话说,我有两个数据。他们是

  1. __import__('__main__')
  2. 'Test.start'

现在,我如何获得应该是t.start(注意这里)的句柄instance<bound method Test.start of <__main__.Test instance at 0xb769678c>>

4

3 回答 3

3

我不确定我是否理解您的问题,但我认为这可以满足您的要求:

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()

module = __import__(t.start.__module__)

print(vars(module)['Test'].start)
print(vars(module)['func'])
print(vars(module)['t'].start)

(Python 3) 输出:

<function Test.start at 0x00E52460>
<function func at 0x00E524F0>
<bound method Test.start of <__main__.Test object at 0x008DF670>>
于 2013-02-12T03:49:20.827 回答
1
obj = __import__(t.start.__module__)    

test_class = getattr(obj, 'Test')

print getattr(test_class, 'start')

我不确定您是否直接从模块中需要它(或者即使可能):/

您还可以使用:

obj = __import__(t.start.__module__)

print obj.__dict__["Test"].__dict__["start"]

但你getattr()这么要求...

于 2013-02-12T00:57:16.147 回答
0

我想知道你为什么写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( )函数的使用给出了相同的结果。

于 2013-02-12T04:26:23.937 回答