0

当直接运行以下脚本时,它会按预期运行:

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

从回溯来看,它与检查有关,但是它也应该在 execfile("classes.py") 或 execfile("classes.py", globals()) 上出错。

那么,就这个错误而言, dict(globals()) != globals() 怎么样,为什么会导致这个错误?

编辑:读者应该参考 Martijn Pieters 和 Lennart Regebro 的答案以获得完整的图片。

4

2 回答 2

3

当您执行 python 文件时,execfile()您正在当前命名空间中执行它。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()

于 2013-07-04T12:45:03.033 回答
1

这个问题不是那个dict(globals()) != globals(),因为它是。这里的问题是,globals()在您执行的上下文execfile()中与globals()在您的classes.py.

当你传入一个命名空间时,你会替换原本会创建的命名空间。如果您传入一个命名空间,您将为classes模块创建一个命名空间。这意味着这__main__将是 classes.py 文件。但是,当您传入execfile()被调用文件的名称空间时,将使用该名称空间,并且__main__将是该模块。

这导致检查无法在源代码中找到类定义,因为它正在查找错误的文件。

如果你传入一个空的命名空间,它根本找不到 a __main__,并且该类将被假定为内置的,没有可用的源,并且会引发一个错误。

总之,你在这里犯的错误是认为globals()解释器是全局的,而实际上它对模块来说是全局的。

从与 Marjtin 的讨论中,很明显 OS X 上的工作方式略有不同。这意味着即使您不传入命名空间,您也不能依赖这种工作方式。

这就引出了一个问题,即你为什么要这样做,以及你实际想要达到的目标。

于 2013-07-04T12:45:47.520 回答