Python元类,execfile,inspect,globals(),命名空间
直接运行以下脚本时,它将按预期操作:Python元类,execfile,inspect,globals(),命名空间,python,metaclass,execfile,Python,Metaclass,Execfile,直接运行以下脚本时,它将按预期操作: import inspect __module__ = "__main__" __file__ = "classes.py" test_str = "test" class met(type): def __init__(cls, name, bases, dct): setattr(cls, "source", inspect.getsource(cls)) #setattr(cls, "source", tes
import inspect
__module__ = "__main__"
__file__ = "classes.py"
test_str = "test"
class met(type):
def __init__(cls, name, bases, dct):
setattr(cls, "source", inspect.getsource(cls))
#setattr(cls, "source", test_str)
super(met, cls).__init__(name, bases, dct)
class ParentModel(object):
__metaclass__ = met
def __init__(self):
super(object, self).__init__(ParentModel.__class__)
def setsource(self):
self.source = inspect.getsource(self.__class__)
#self.source = test_str
def getsource(self):
return self.source
class ChildB(ParentModel):
name = "childb"
pass
class ChildA(ChildB):
name = "childa"
pass
class ChildC(ChildA):
name = "childc"
pass
当试图通过pythonshell或其他脚本中的exec或execfile运行此脚本时,会出现困难。例如:
>>> execfile("classes.py")
运行时没有问题,但是:
>>> ns = {}
>>> execfile("classes.py", ns)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "classes.py", line 13, in <module>
class ParentModel(object):
File "classes.py", line 9, in __init__
setattr(cls, "source", inspect.getsource(cls))
File "D:\Python27\lib\inspect.py", line 701, in getsource
lines, lnum = getsourcelines(object)
File "D:\Python27\lib\inspect.py", line 690, in getsourcelines
lines, lnum = findsource(object)
File "D:\Python27\lib\inspect.py", line 526, in findsource
file = getfile(object)
File "D:\Python27\lib\inspect.py", line 408, in getfile
raise TypeError('{!r} is a built-in class'.format(object))
TypeError: <module '__builtin__' (built-in)> is a built-in class
同样,运行没有问题,但:
>>> ns = dict(globals())
>>> execfile("classes.py", ns)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "classes.py", line 13, in <module>
class ParentModel(object):
File "classes.py", line 9, in __init__
setattr(cls, "source", inspect.getsource(cls))
File "D:\Python27\lib\inspect.py", line 701, in getsource
lines, lnum = getsourcelines(object)
File "D:\Python27\lib\inspect.py", line 690, in getsourcelines
lines, lnum = findsource(object)
File "D:\Python27\lib\inspect.py", line 526, in findsource
file = getfile(object)
File "D:\Python27\lib\inspect.py", line 408, in getfile
raise TypeError('{!r} is a built-in class'.format(object))
TypeError: <module '__builtin__' (built-in)> is a built-in class
ns=dict(globals())
>>>execfile(“classes.py”,ns)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“classes.py”,第13行,在
类ParentModel(对象):
文件“classes.py”,第9行,在_init中__
setattr(cls,“源”,inspect.getsource(cls))
getsource中第701行的文件“D:\Python27\lib\inspect.py”
行,lnum=getsourcelines(对象)
getsourcelines中第690行的文件“D:\Python27\lib\inspect.py”
行,lnum=findsource(对象)
findsource中第526行的文件“D:\Python27\lib\inspect.py”
file=getfile(对象)
getfile中第408行的文件“D:\Python27\lib\inspect.py”
raise TypeError(“{!r}是内置类”。格式(对象))
TypeError:是一个内置类
从回溯来看,它与inspect相关,但是它也应该在execfile(“classes.py”)或execfile(“classes.py”,globals())上出错
那么,就这个错误而言,dict(globals())如何呢globals()以及为什么会导致此错误
编辑:读者应参考Martijn Pieters和Lennart Regebro的答案以获得完整的图片。使用
execfile()
执行python文件时,您是在当前名称空间中执行它。REPL命名空间是一个内置模块:
>>> import sys
>>> sys.modules['__main__']
<module '__main__' (built-in)>
您的下一个问题是,因为您使用的是execfile
,所以为代码设置的模块总是会出错inspect.getsource()
无法确定在何处定义代码,因为execfile()
绕过了正常的导入机制:
$ cat test.py
execfile('classes.py')
$ python test.py
Traceback (most recent call last):
File "test.py", line 1, in <module>
execfile('classes.py')
File "classes.py", line 13, in <module>
class ParentModel(object):
File "classes.py", line 9, in __init__
setattr(cls, "source", inspect.getsource(cls))
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 701, in getsource
lines, lnum = getsourcelines(object)
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 690, in getsourcelines
lines, lnum = findsource(object)
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 564, in findsource
raise IOError('could not find class definition')
IOError: could not find class definition
为了明确起见,创建
globals()
的副本只能防止execfile()
修改当前模块名称空间(或REPL名称空间)。如果在当前名称空间中使用execfile()。REPL命名空间是一个内置模块:
>>> import sys
>>> sys.modules['__main__']
<module '__main__' (built-in)>
您的下一个问题是,因为您使用的是execfile
,所以为代码设置的模块总是会出错inspect.getsource()
无法确定在何处定义代码,因为execfile()
绕过了正常的导入机制:
$ cat test.py
execfile('classes.py')
$ python test.py
Traceback (most recent call last):
File "test.py", line 1, in <module>
execfile('classes.py')
File "classes.py", line 13, in <module>
class ParentModel(object):
File "classes.py", line 9, in __init__
setattr(cls, "source", inspect.getsource(cls))
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 701, in getsource
lines, lnum = getsourcelines(object)
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 690, in getsourcelines
lines, lnum = findsource(object)
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 564, in findsource
raise IOError('could not find class definition')
IOError: could not find class definition
为了明确起见,创建globals()
的副本只能防止execfile()
修改当前模块名称空间(或REPL名称空间)。否则,传递给execfile()
的字典之间没有区别。这个问题不是dict(globals())!=globals()
,因为它是。这里的问题是,您执行的execfile()
上下文中的globals()
与classes.py中的globals()
不同
当您传入一个名称空间时,您将替换否则将创建的名称空间。如果传入一个名称空间,您将为类
模块创建一个名称空间。这意味着\uuuu main\uuuu
将是classes.py文件。但是,当您传入调用execfile()
的文件的名称空间时,将使用该名称空间,而\uuuuu main\uuuu
将是该模块
这导致inspect无法在源代码中找到类定义,因为它在错误的文件中查找
如果传入一个空名称空间,它将根本找不到\uuuuu main\uuuuu
,并且该类将被假定为一个内置类,没有可用的源代码,并且会引发一个错误
总之,您在这里犯的错误是认为globals()
对于解释器是全局的,而实际上对于模块是全局的
从与Marjtin的讨论中,很明显,在OSX上的工作方式略有不同。这意味着即使不传入名称空间,也不能依赖这种工作方式
这就引出了一个问题,你为什么要这样做,以及你真正想要实现的目标。这个问题不是dict(globals())!=globals()
,因为它是。这里的问题是,您执行的execfile()
上下文中的globals()
与classes.py中的globals()
不同
当您传入一个名称空间时,您将替换否则将创建的名称空间。如果传入一个名称空间,您将为类
模块创建一个名称空间。这意味着\uuuu main\uuuu
将是classes.py文件。但是,当您传入调用execfile()
的文件的名称空间时,将使用该名称空间,而\uuuuu main\uuuu
将是该模块
这导致inspect无法在源代码中找到类定义,因为它在错误的文件中查找
如果传入一个空名称空间,它将根本找不到\uuuuu main\uuuuu
,并且该类将被假定为一个内置类,没有可用的源代码,并且会引发一个错误
总之,您在这里犯的错误是认为globals()
对于解释器是全局的,而实际上对于模块是全局的
从与Marjtin的讨论中,很明显,在OSX上的工作方式略有不同。这意味着即使不传入名称空间,也不能依赖这种工作方式
这就引出了一个问题,你为什么要这样做,你到底想达到什么目的。事实上,事实并非如此。如果您创建第二个文件,并调用execfile('classes.py',globals())
,您会发现它引发了一个错误。对不起,Martijn,
>>> import sys
>>> import types
>>> sys.modules['fake_classes'] = types.ModuleType('fake_classes')
>>> sys.modules['fake_classes'].__file__='classes.py'
>>> ns = {'__name__': 'fake_classes'}
>>> execfile('classes.py', ns)
>>> >>> ns.keys()
['__module__', 'ChildA', '__builtins__', 'inspect', '__package__', 'met', 'ChildB', 'ChildC', 'ParentModel', '__name__', 'test_str']