2013-02-16 1 views
7

글로벌 변수 x을 변경해야하는 세 가지 함수를 정의했습니다."global"과 "import __main__"의 차이

def changeXto1(): 
    global x 
    x = 1 

def changeXto2(): 
    from __main__ import x 
    x = 2 

def changeXto3(): 
    import __main__ 
    __main__.x = 3 

x = 0 
print x 
changeXto1() 
print x 
changeXto2() 
print x 
changeXto3() 
print x 

그것은 결과를 제공합니다

0 
1 
1 
3 

changeXto1가 정상 글로벌 문을 사용합니다. 결과는 예상대로입니다. x == 1. changeXto2x을 처리하기 위해 from __main__ import을 사용합니다. 이것은 작동하지 않습니다. 그 이후에 x은 여전히 ​​1입니다. changeXto3x을 주소로 사용하여 __main__.x을 사용하여 import main을 사용합니다. 이후 결과는 예상대로 3입니다.

from __main__ importchangeXto2에서 작동하는 이유는 무엇입니까? import __main__changeXto3에서 작동합니까? __main__ 모듈로 전역 변수를 처리 할 수 ​​있다면 파이썬에서 글로벌 선언문이 필요한 이유는 무엇입니까?

+0

changeXto2는 지역 변수를 설정 http://www.saltycrane.com/blog/2008/01/python-variable- scope-notes/ –

+0

modulesimporting 가져 오기에 관한 내용이 없습니다. –

답변

10

이것은 파이썬이 코드를 바이트 코드로 변환하는 방법과 관련이 있습니다 (컴파일 단계).

함수를 컴파일 할 때 Python은 로컬 변수로 할당 된 모든 변수를 처리하고 최적화를 수행하여 수행해야 할 이름 조회 횟수를 줄입니다. 각 지역 변수에 인덱스가 할당되고 함수가 호출되면 값이 인덱스로 지정된 스택 로컬 배열에 저장됩니다. 컴파일러는 변수에 액세스하기 위해 LOAD_FASTSTORE_FAST opcode를 내 보냅니다.

global 구문은 대신 변수에 값이 할당 되더라도 로컬 변수로 간주되어서는 안되며 인덱스를 할당하면 안된다는 것을 컴파일러에게 알려줍니다. 대신 변수에 액세스하려면 LOAD_GLOBALSTORE_GLOBAL opcode를 사용합니다. 이러한 연산 코드는 이름을 사용하여 많은 사전 (지역, 전역)에서 조회를 수행하기 때문에 속도가 느립니다.

값을 읽는 변수 만 액세스하는 경우 컴파일러는 로컬 변수 또는 전역 변수인지 여부를 모르기 때문에 항상 LOAD_GLOBAL을 내고 따라서 전역 변수라고 가정합니다.

첫 번째 함수에서 global x을 사용하면 x에 대한 쓰기 액세스를 로컬 변수 대신 전역 변수에 쓰기로 처리하도록 컴파일러에 알립니다. 함수의 연산 코드는 명확하게 :

세 번째 예에서
>>> dis.dis(changeXto1) 
    3   0 LOAD_CONST    1 (1) 
       3 STORE_GLOBAL    0 (x) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE   

, 당신은 __main__라는 이름의 지역 변수에 __main__ 모듈을 가져온 다음의 x 필드에 할당. 모듈은 모든 최상위 매핑을 필드로 저장하는 객체이므로 __main__ 모듈의 변수 x에 할당합니다. 그리고 찾은 것처럼 코드가 __main__ 모듈에 정의되어 있기 때문에 __main__ 모듈 필드는 globals() 사전의 값과 직접 매핑됩니다.

>>> dis.dis(changeXto3) 
    2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    0 (None) 
       6 IMPORT_NAME    0 (__main__) 
       9 STORE_FAST    0 (__main__) 

    3   12 LOAD_CONST    2 (3) 
      15 LOAD_FAST    0 (__main__) 
      18 STORE_ATTR    1 (x) 
      21 LOAD_CONST    0 (None) 
      24 RETURN_VALUE   

두 번째 예는 재미있다 : 옵 코드는 직접 x에 액세스하지 않는 것으로 나타났다. x 변수에 값을 할당하므로 컴파일러는 변수가 로컬 변수라고 가정하고 최적화를 수행합니다. 그런 다음 from __main__ import x은 모듈 __main__을 가져오고 모듈 __main__x의 값을 x이라는 로컬 변수에 바인딩하는 새 바인딩을 만듭니다. 이것은 항상 그렇습니다. from ${module} import ${name}은 현재 바인딩을 새 네임 스페이스로 만듭니다. 변수 x에 새 값을 할당하면 관련이없는 모듈 __main__의 바인딩이 아니라 현재 바인딩이 변경됩니다 (값이 변경 가능하고 변경하면 모든 바인딩을 통해 변경 내용이 표시됨). 여기 옵 코드입니다 :

>>> dis.dis(f2) 
    2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    2 (('x',)) 
       6 IMPORT_NAME    0 (__main__) 
       9 IMPORT_FROM    1 (x) 
      12 STORE_FAST    0 (x) 
      15 POP_TOP    

    3   16 LOAD_CONST    3 (2) 
      19 STORE_FAST    0 (x) 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE   

이 생각하는 좋은 방법은 파이썬의 모든 할당은 사전에 값에 이름을 결합하고, 역 참조 그냥 사전 조회를하고있는 것입니다 (이것은 대략적인 근사치입니다 , 개념적 모델에 꽤 가깝다). obj.field을 수행 할 때 "field" 키의 obj (obj.__dict__을 통해 액세스 가능)의 숨겨진 사전을 찾고 있습니다.

네이 키드 변수 이름이있는 경우 locals() 사전에서 찾은 다음 globals() 사전이 다른 경우 (모듈 수준에서 코드가 실행될 때 동일 함) 룩업됩니다. 할당의 경우 global ${name} (이 구문은 최상위에서 작동 함)을 수행하여 전역 액세스를 원한다고 선언하지 않는 한 항상 바인딩을 locals() 사전에 넣습니다.

그래서 함수를 번역, 이것은 당신이 쓴 경우 거의이다

# NOTE: this is valid Python code, but is less optimal than 
# the original code. It is here only for demonstration. 

def changeXto1(): 
    globals()['x'] = 1 

def changeXto2(): 
    locals()['x'] = __import__('__main__').__dict__['x'] 
    locals()['x'] = 2 

def changeXto3(): 
    locals()['__main__'] = __import__('__main__') 
    locals()['__main__'].__dict__['x'] = 3 
+0

매우 포괄적 인 답변에 감사드립니다. 나는 항상 변수가 다르게 호출된다는 것을 제외하고는'import module '과'module import from'이 같다고 생각했다. 새로운 변수가'from import'를 사용하여 생성 될 때 차이점이 있음을 알았습니다. 원본 모듈의 변수는 동일하게 유지되지만 로컬 네임 스페이스의 변수가 새로 만들어져 로컬 이름에 바인딩됩니다. – Holger

8

import __main__ 동안 changeXto2에서 from __main__ import 작품은 changeXto3에 노력하지 않는 이유는 무엇입니까?

잘 작동합니다. 원하는대로 할 수 없습니다. __main__의 네임 스페이스에 액세스하는 대신 로컬 네임 스페이스에 이름과 값을 복사합니다.

__main__ 모듈에서도 전역 변수를 처리 할 수있는 경우 왜 파이썬에서 전역 명령문이 필요합니까?

코드가 __main__에서 실행 중일 때 그들은 단지 같은 일 을하기 때문에. 가져온 후 othermodule을 실행중인 경우 __main__은 기본 스크립트를 나타내고 othermodule이 아닙니다.