直接运行以下脚本时,它按预期运行:
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
尝试通过python shell或其他脚本中的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
这会导致错误,如果execfile的全局命名空间参数接受了字典,则会出现混乱。但是:
>>> execfile("classes.py", globals())
同样,运行没有问题,但是:
>>> 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
从回溯中,它与inspect相关,但是它应该在execfile(“classes.py”)或execfile(“classes.py”,globals())上出错。
那么,就这个错误而言,dict(globals())如何!= globals()以及为什么会导致这个错误?
编辑:读者应该参考Martijn Pieters和Lennart Regebro的答案以获得完整的图片。
答案 0 :(得分:2)
当您使用execfile()
执行python文件时,您正在当前命名空间中执行它。 REPL命名空间是一个内置模块:
>>> import sys
>>> sys.modules['__main__']
<module '__main__' (built-in)>
表示inspect.getsource()
有没有源文件要检索:
>>> sys.modules['__main__'].__file__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__file__'
>>> import inspect
>>> inspect.getfile(sys.modules['__main__'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 403, in getfile
raise TypeError('{!r} is a built-in module'.format(object))
TypeError: <module '__main__' (built-in)> is a built-in module
您的下一个问题是因为您使用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
除非您直接从具有相同源代码的文件中运行,否则您的代码不会与execfile('classes.py', globals())
一起使用。在某些平台上,它似乎可行,因为execfile
最终会触发设置当前模块的__file__
属性的代码。
实际完成这项工作的唯一方法是
sys.modules
中创建一个假模块对象,并为其指定__file__
属性,指向classes.py
。execfile()
,将__name__
设置为与假模块对象匹配。演示:
>>> 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']
为了使其明确,创建globals()
的副本只会阻止execfile()
修改当前模块名称空间(或您的REPL名称空间)。传递给execfile()
的词典之间没有区别。
答案 1 :(得分:1)
这个问题不是dict(globals()) != globals()
,因为它是。{1}}。这里的问题是,您执行globals()
的上下文中的execfile()
与您globals()
中的classes.py
不同。
当您传入命名空间时,您将替换原本将创建的命名空间。如果您传入名称空间,则会为classes
模块创建一个名称空间。这意味着__main__
将是classes.py文件。但是,当您传入调用execfile()
的文件的名称空间时,将使用该名称空间,而__main__
将是该模块。
这会导致检查无法在源代码中找到类定义,因为它查找的文件不正确。
如果传入一个空名称空间,它将根本找不到__main__
,并且该类将被假定为内置函数,没有可用的源代码,并且将引发该效果的错误。
总之,你在这里犯的错误是想到globals()
并且对解释者来说是全球性的,当它实际上是模块的全局时。
通过与Marjtin的讨论,可以清楚地知道OS X上的工作方式略有不同。这意味着即使您没有传入命名空间,也无法依赖此工作。
然后,这会导致你为什么这样做,以及你实际上想要实现的目标。