2013-07-04 3 views
0

검사 다음의 임원 또는 execfile를 통해이 스크립트를 실행하려고 할 때파이썬 메타 클래스, 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", 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 

어려움이 발생 파이썬 셸 또는 다른 스크립트. 그러나 문제없이

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

, 그것의, 그러나 그것은 ("classes.py") 또는 execfile execfile에 오류가한다 검사 관련 (" classes.py ", globals())도 사용할 수 있습니다.

이 오류와 관련하여 dict (globals())! = globals()가 어떻게 발생하며이 오류가 발생하는 이유는 무엇입니까?

편집 : 독자는 Martijn Pieters와 Lennart Regebro의 답변을 참조하여 전체 그림을 찾아야합니다.

답변

2

execfile()으로 파이썬 파일을 실행할 때 현재 네임 스페이스에서 실행 중입니다. REPL 네임 스페이스가 내장 된 모듈입니다 :

>>> import sys 
>>> sys.modules['__main__'] 
<module '__main__' (built-in)> 

있다는 것을 의미 inspect.getsource()에 대한은 SourceFile는 검색 없습니다 :

>>> 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모듈을 사용하기 때문에 귀하의 코드에 대한 설정은 항상 잘못 될 것입니다. 코드를 정의 곳 execfile() 정상적인 수입 메커니즘을 우회으로 inspect.getsource()는 판별 할 수없는 시점에서

$ 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에 가짜 모듈 객체를 생성하고 그것을 classes.py를 가리키는 __file__ 속성을 제공합니다.
  • 가짜 모듈 객체와 일치하도록 __name__을 설정하는 execfile()으로 네임 스페이스를 전달합니다.

데모 :

>>> 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'] 

그냥 만 현재 모듈 네임 스페이스 (또는 REPL 네임 스페이스)를 수정에서 execfile()을 방지 globals()의 복사본을 생성, 그것은 명시 적으로 확인합니다. 그렇지 않으면 execfile()에 전달되는 사전 사이에 차이가 없습니다.

+0

사실, 그렇지 않습니다. 두 번째 파일을 만들고'execfile ('classes.py', globals())를 호출하면 에러가 발생합니다. –

+0

@ LenartRegebro : 나는 해답을 이미 수정했다. –

+0

죄송합니다, Martijn, 나는 틀렸다고 생각합니다. 문제는 그것이 실행 된 네임 스페이스가 클래스 모듈의'globals()'가 아닐 때이다. 이것은 인터프리터에서 실행하거나 execfile하고 네임 스페이스를 전달할 때 발생합니다. 그러나 네임 스페이스를 전달하지 않으면 작동합니다. –

1

이 문제는 dict(globals()) != globals()이 아니기 때문에 발생하지 않습니다. 여기서 문제는 execfile()을 실행하는 컨텍스트의 globals()classes.py에서와 같이 globals()과 같지 않다는 것입니다.

네임 스페이스를 전달할 때 생성되는 네임 스페이스를 바꿉니다. 네임 스페이스를 전달하면 classes 모듈에 대해 생성 된 네임 스페이스를 얻게됩니다. 즉, __main__이 classes.py 파일이됩니다. 그러나 execfile()이 호출 된 파일의 네임 스페이스를 전달하면 해당 네임 스페이스가 대신 사용되며 __main__이 해당 모듈이됩니다.

잘못된 파일을보고 있기 때문에 검사를 통해 소스 코드에서 클래스 정의를 찾지 못하게됩니다.

빈 네임 스페이스를 전달하면 __main__이 전혀 발견되지 않으며 클래스는 소스가없는 내장 함수로 간주되며 그 결과에 대한 오류가 발생합니다.

요약하면 여기서 실수는 globals()을 생각해보십시오. 실제로 인터프리터에게는 전역적일 때입니다.

Marjtin과의 토론에서 OS X에서 약간 다른 방식으로 작동한다는 것이 확실합니다. 즉, 네임 스페이스를 통과하지 않아도이 작업에 의존 할 수 없음을 의미합니다.

그런 다음 왜이 작업을 수행하고 있는지, 그리고 실제로 달성하려는 작업의 문제가 발생합니다.

+0

나는 무언가를 몹시, 몹시 dodgy 달성하려고 노력하고있다. 그러나 도움은 많이 감사합니다. – dilbert

+0

@ dilbert : 요점은 아마 더 좋은 방법이 있다는 것입니다. –

+0

글쎄요, 당신이 운영하는 시스템과 더 잘 어울리는 접근 방법을 찾는 것이 최선의 방법이라고 생각합니다. 그러나 이것은 약간 다릅니다. 나는 연장 의향이있다 : http://stackoverflow.com/questions/16907186/python-model-inheritance-and-order-of-model-declaration. – dilbert