가져온 모듈의 전역 변수 가시성


112

Python 스크립트에서 모듈을 가져 오는 벽에 부딪 혔습니다. 오류를 설명하기 위해 최선을 다하고 오류가 발생하는 이유와 문제를 해결하기 위해이 특정 접근 방식을 사용하는 이유 (잠시 후 설명하겠습니다) :

이 보조 모듈을 가져올 네임 스페이스에 정의 된 엔터티를 참조하는 일부 유틸리티 함수 / 클래스를 정의한 모듈이 있다고 가정 해 보겠습니다 ( "a"를 이러한 엔터티로 지정).

module1 :

def f():
    print a

그리고 "a"가 정의 된 메인 프로그램이 있는데,이 프로그램으로 유틸리티를 가져 오려고합니다.

import module1
a=3
module1.f()

프로그램을 실행하면 다음 오류가 발생합니다.

Traceback (most recent call last):
  File "Z:\Python\main.py", line 10, in <module>
    module1.f()
  File "Z:\Python\module1.py", line 3, in f
    print a
NameError: global name 'a' is not defined

비슷한 질문이 과거 (이틀 전 d' uh)에 제기되었으며 몇 가지 해결책이 제안되었지만 이것이 제 요구 사항에 맞지 않는다고 생각합니다. 내 특정 컨텍스트는 다음과 같습니다.

MySQL 데이터베이스 서버에 연결하고 GUI로 데이터를 표시 / 수정하는 Python 프로그램을 만들려고합니다. 청결을 위해 별도의 파일에 보조 / 유틸리티 MySQL 관련 함수를 정의했습니다. 그러나 그들은 모두 내가 원래 유틸리티 모듈 내부 에 정의 하고 MySQLdb 모듈 의 커서 객체 인 공통 변수를 가지고 있습니다. 나중에 db 서버와 통신하는 데 사용되는 커서 개체가 주 모듈에 정의되어 주 모듈과 여기로 가져온 모든 것이 해당 개체에 액세스 할 수 있어야 한다는 것을 나중에 깨달았습니다 .

최종 결과는 다음과 같습니다.

utility_module.py :

def utility_1(args):
    code which references a variable named "cur"
def utility_n(args):
    etcetera

그리고 내 주요 모듈 :

program.py :

import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

그리고 유틸리티 함수를 호출하려고하면 앞서 언급 한 "글로벌 이름이 정의되지 않음"오류가 발생합니다.

특별한 제안은 다음과 같이 유틸리티 파일에 "from program import cur"문을 포함하는 것이 었습니다.

utility_module.py :

from program import cur
#rest of function definitions

program.py :

import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

그러나 그것은 주기적 수입이나 그와 비슷한 것이고, 결론적으로도 충돌합니다. 그래서 제 질문은 :

도대체 어떻게 메인 모듈에 정의 된 "cur"객체를 가져 오는 보조 기능에 보이게 할 수 있습니까?

시간을 내 주셔서 감사합니다. 솔루션이 다른 곳에 게시 된 경우 진심으로 사과드립니다. 나는 스스로 답을 찾을 수 없으며 책에 더 이상 속임수가 없습니다.


1
업데이트에 따라 : 어쨌든 단일 공유 커서를 원하지 않을 것입니다. 단일 공유 연결 , 예,하지만 커서는 저렴하며 동시에 여러 커서를 활성화해야하는 좋은 이유가 있습니다 (예 : 대신 fetch_all두 개의 목록을 반복하는 대신 잠금 단계에서 두 개의 커서를 반복 할 수 있습니다. 또는 충돌없이 데이터베이스를 사용하여 두 개의 다른 스레드 / 그린 렛 / 콜백 체인 / 무엇이든 사용할 수 있습니다.
abarnert

어쨌든, 당신이 공유하고 싶은대로, 내가 여기에 답이 이동하는 것입니다 생각 db(그리고 cur, 당신이 주장하는 경우) 모두가 별도의 모듈로 programutilities_module에서 가져옵니다. 이렇게하면 순환 종속성 (프로그램이 가져 오는 모듈에서 프로그램 가져 오기)과 그에 따른 혼동이 발생하지 않습니다.
abarnert

답변:


244

Python의 전역은 모든 모듈이 아닌 모듈 에 전역입니다. (예를 들어 C에서 전역은 명시 적으로 만들지 않는 한 모든 구현 파일에서 동일하기 때문에 많은 사람들이 이것을 혼동합니다 static.)

실제 사용 사례에 따라이를 해결하는 방법에는 여러 가지가 있습니다.


이 길을 가기 전에 이것이 정말로 글로벌해야하는지 스스로에게 물어보십시오. f그냥 자유 함수가 아닌 인스턴스 메서드로 클래스를 원 할까요? 그런 다음 다음과 같이 할 수 있습니다.

import module1
thingy1 = module1.Thingy(a=3)
thingy1.f()

정말로 전역을 원하지만에서 사용하기 위해 거기에 있다면 module1해당 모듈에서 설정하십시오.

import module1
module1.a=3
module1.f()

반면에 a많은 모듈에서 공유되는 경우 다른 곳에 배치하고 모두가 가져 오도록합니다.

import shared_stuff
import module1
shared_stuff.a = 3
module1.f()

… 그리고 module1.py에서 :

import shared_stuff
def f():
    print shared_stuff.a

from변수가 상수가 아닌 경우 가져 오기를 사용 하지 마십시오 . 가져올 때 참조 된 항목으로 초기화 from shared_stuff import a된 새 a변수를 만들고이 shared_stuff.aa변수는에 대한 할당의 영향을받지 않습니다 shared_stuff.a.


또는 드문 경우에 내장 모듈과 같이 모든 곳에서 진정으로 전역 적이어야하는 경우 내장 모듈에 추가하십시오. 정확한 세부 사항은 Python 2.x와 3.x간에 다릅니다. 3.x에서는 다음과 같이 작동합니다.

import builtins
import module1
builtins.a = 3
module1.f()

12
훌륭하고 포괄적입니다.
kindall

답변 해 주셔서 감사합니다. 주 모듈에 정의 된 변수가 실제로 전역 적이 지 않다는 사실을 극복 할 수는 없지만 import shared_stuff 접근 방식을 시도해 볼 것 입니다. 프로그램의 어디에서나 모든 사람이 이름을 진정으로 액세스 할 수있는 방법이 없습니까? ?
Nubarke 2013

1
"모든 프로그램에서 모든 사람이 액세스 할 수있는"무언가를 갖는 것은 python선종에 반하는 것 입니다 . 특히 "명시적인 것이 암시적인 것보다 낫습니다". 파이썬은 매우 잘 생각한 디자인을 가지고 있으며, 잘 사용한다면 글로벌 키워드를 다시 사용할 필요없이 파이썬 경력의 나머지 부분을 관리 할 수 ​​있습니다.
Bi Rico

1
업데이트 : 감사합니다! shared_stuff 접근 방식은 잘 작동했으며 다른 문제를 해결할 수 있다고 생각합니다.
Nubarke 2013

1
@DanielArmengod : 정말 필요한 경우를 대비하여 기본 답변을 추가했습니다. (더 자세한 정보가 필요하면 검색하세요. 빌트인에 항목을 올바르게 추가하는 방법에 대해 최소한 두 가지 질문이 있습니다.)하지만 Bi Rico가 말했듯이, 거의 확실히 필요하거나 원하지 않습니다.
abarnert

9

해결 방법으로 다음과 같이 외부 계층에서 환경 변수를 설정하는 것을 고려할 수 있습니다.

main.py :

import os
os.environ['MYVAL'] = str(myintvariable)

mymodule.py :

import os

myval = None
if 'MYVAL' in os.environ:
    myval = os.environ['MYVAL']

추가 예방 조치로 MYVAL이 모듈 내부에 정의되어 있지 않은 경우 처리하십시오.


5

함수는 정의 된 모듈의 전역을 사용합니다 . a = 3예를 들어 를 설정하는 대신을 설정 해야합니다 module1.a = 3. 따라서에서 cur글로벌로 utilities_module사용하려면utilities_module.cur .

더 나은 해결책 : 전역을 사용하지 마십시오. 필요한 변수를 필요한 함수에 전달하거나 모든 데이터를 함께 묶는 클래스를 만들고 인스턴스를 초기화 할 때 전달합니다.


'import module1'을 작성하는 대신 사용자가 'from module1 import f'를 작성했다면 f는 main.py의 글로벌 네임 스페이스에 올 것입니다. 이제 main.py에서 f ()를 사용하면 a = 3과 f (함수 정의)가 모두 main의 전역 네임 스페이스에 있습니다. 이것이 해결책입니까? 내가 틀렸다면이 주제에 대한 기사를
변수

위의 접근 방식을 사용하고 전역을 사용하는 클래스를 인스턴스화 할 때 전역을 인수로 전달했습니다. 이것은 괜찮 았지만 나중에 코드의 Sonarqube 코드 검사를 활성화했고 함수에 너무 많은 인수가 있다는 불평이있었습니다. 그래서 우리는 다른 해결책을 찾아야했습니다. 이제 우리는이를 필요로하는 각 모듈에서 읽는 환경 변수를 사용합니다. 실제로 OOP를 준수하지는 않지만 그게 다입니다. 코드 실행 중에 전역이 변경되지 않는 경우에만 작동합니다.
rimetnac

5

이 게시물은 내가 만난 Python 동작에 대한 관찰 일뿐입니다. 위에서 읽은 조언이 아래에서했던 것과 똑같은 일을했다면 효과가 없을 수도 있습니다.

즉, 전역 / 공유 변수를 포함하는 모듈이 있습니다 (위에서 제안한대로).

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

그런 다음 공유 항목을 가져 오는 기본 모듈이 있습니다.

import sharedstuff as shared

그리고 실제로 이러한 어레이를 채운 다른 모듈. 이들은 메인 모듈에 의해 호출됩니다. 이러한 다른 모듈을 종료 할 때 어레이가 채워져 있음을 분명히 알 수 있습니다. 그러나 기본 모듈에서 다시 읽을 때 비어 있습니다. 이것은 나에게 다소 이상했습니다 (글쎄요, 저는 Python을 처음 사용했습니다). 그러나 주 모듈에서 sharedstuff.py를 가져 오는 방법을 다음과 같이 변경하면

from globals import *

작동했습니다 (배열이 채워졌습니다).

그냥 말해


2

이 특정 문제에 대한 가장 쉬운 해결책은 모듈 전역의 변수에 커서를 저장했을 모듈 내에 다른 함수를 추가하는 것입니다. 그러면 다른 모든 기능도 사용할 수 있습니다.

module1 :

cursor = None

def setCursor(cur):
    global cursor
    cursor = cur

def method(some, args):
    global cursor
    do_stuff(cursor, some, args)

주요 프로그램 :

import module1

cursor = get_a_cursor()
module1.setCursor(cursor)
module1.method()

2

전역은 모듈에 따라 다르기 때문에 가져온 모든 모듈에 다음 함수를 추가 한 다음 사용할 수 있습니다.

  • 단일 변수 (사전 형식)를 전역 변수로 추가합니다.
  • 메인 모듈 전역을 그것으로 전송하십시오 .

addglobals = 람다 x : globals (). update (x)

그런 다음 현재 전역에 전달해야하는 것은 다음과 같습니다.

가져 오기 모듈

module.addglobals (globals ())


1

위의 답변에서 보지 global_dict못했기 때문에 호출 모듈의 전역을 필요로하는 함수에 인수를 추가 한 다음 호출 할 때 함수에 dict를 전달하는 간단한 해결 방법을 추가 할 것이라고 생각했습니다 . 예 :

# external_module
def imported_function(global_dict=None):
    print(global_dict["a"])


# calling_module
a = 12
from external_module import imported_function
imported_function(global_dict=globals())

>>> 12

0

이를 수행하는 OOP 방식은 모듈을 바인딩되지 않은 메소드 세트 대신 클래스로 만드는 것입니다. 그런 다음 __init__또는 setter 메서드를 사용하여 모듈 메서드에서 사용할 호출자의 변수를 설정할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.