교차 모듈 변수를 만드는 방법은 무엇입니까?


122

__debug__는 모든 모듈에 영향을주기 때문에 변수는 부분에 편리합니다. 같은 방식으로 작동하는 다른 변수를 생성하려면 어떻게해야합니까?

하나의 모듈에서 foo를 변경하면 다른 모듈에서 업데이트된다는 점에서 변수 (원래 형이고 'foo'라고 부릅니다)는 진정한 글로벌 일 필요가 없습니다. 다른 모듈을 가져 오기 전에 foo를 설정할 수 있으면 괜찮을 것입니다. 그러면 동일한 값이 표시됩니다.

답변:


114

나는이 솔루션을 어떤 방식, 형태 또는 형태로도지지하지 않습니다. 그러나 __builtin__모듈에 변수를 추가하면 __builtin__기본적으로 모두 포함 된 다른 모듈에서 전역 인 것처럼 액세스 할 수 있습니다 .

a.py는

print foo

b.py는

import __builtin__
__builtin__.foo = 1
import a

그 결과 "1"이 인쇄됩니다.

편집 :__builtin__ 모듈이 지역의 상징으로 볼 수 있습니다 __builtins__-이 답변 둘 사이의 차이에 대한 이유입니다. 또한 python3에서 __builtin__로 이름이 변경되었습니다 builtins.


2
이 상황이 마음에 들지 않는 이유는 무엇입니까?
Software Enthusiastic

31
우선, 코드를 읽을 때 사람들의 기대를 깨뜨립니다. "여기서 사용되는 'foo'기호는 무엇입니까? 정의 된 위치를 볼 수없는 이유는 무엇입니까?"
Curt Hagenlocher

9
또한 향후 버전의 Python이 실제 내장으로 선택한 이름을 사용하기 시작하면 혼란을 일으킬 수 있습니다.
intuited

4
이것은 가져온 모듈과 db 연결을 공유하는 것과 같은 것에 대한 좋은 솔루션입니다. 온 전성 검사로 가져온 모듈이 hasattr(__builtin__, "foo").
Mike Ellis

4
이 답변을 읽는 사람 : DONT! 하다 ! 이 ! 정말,하지 마세요.
bruno desthuilliers

161

전역 교차 모듈 변수가 필요한 경우 간단한 전역 모듈 수준 변수로 충분할 것입니다.

a.py :

var = 1

b.py :

import a
print a.var
import c
print a.var

c.py :

import a
a.var = 2

테스트:

$ python b.py
# -> 1 2

실제 예 : Django의 global_settings.py (Django 앱 설정은 개체 를 가져 오는 데 사용됨 django.conf.settings).


3
가능한 네임 스페이스 충돌을 방지하기 때문에 더
좋습니다

가져 오는 모듈 (이 경우 a.py)에 main()? 그게 그렇게 중요한 건가?
sedeh

4
@sedeh : 아니요. a.py가 스크립트로도 실행되는 경우 if __name__=="__main__"가드 를 사용 하여 가져올 때 예기치 않은 코드 실행을 방지하십시오.
jfs

6
현실 세계에서는이 솔루션에 대해 약간주의해야합니다. 프로그래머가 '가져 오기 변수에서'를 사용하여 '전역'변수를 선택하면 (c.py에서이 변형을 시도해보십시오) 가져올 때 변수의 복사본을 얻습니다.
Paul Whipp

1
@PaulWhipp : 잘못됨 (힌트 : id()신원 확인에 사용 )
jfs

25

나는 그것이 의미가있는 많은 상황이 있다고 믿고 그것은 몇 개의 (단단하게 결합 된) 모듈에 걸쳐 알려진 몇몇 전역을 가지도록 프로그래밍을 단순화합니다. 이 정신에서 나는 그들을 참조 할 필요가있는 모듈들에 의해 반입되는 글로벌 모듈을 갖는 아이디어에 대해 조금 더 자세히 설명하고 싶습니다.

그러한 모듈이 하나 뿐인 경우 "g"로 이름을 지정합니다. 여기에서 전역으로 처리하려는 모든 변수에 기본값을 할당합니다. 이들 중 하나를 사용하는 각 모듈에서는 "from g import var"를 사용하지 않습니다. 이는 가져 오기시에만 g에서 초기화되는 지역 변수를 생성하기 때문입니다. 대부분의 참조는 g.var 및 "g"형식으로 작성합니다. 다른 모듈에서 잠재적으로 액세스 할 수있는 변수를 다루고 있음을 지속적으로 상기시켜줍니다.

이러한 전역 변수의 값이 모듈의 일부 함수에서 자주 사용되는 경우 해당 함수는 로컬 복사본을 만들 수 있습니다. var = g.var. 그러나 var에 대한 할당은 로컬이고 전역 g.var는 할당에서 명시 적으로 g.var를 참조하지 않고는 업데이트 할 수 없다는 것을 인식하는 것이 중요합니다.

모듈의 다른 하위 집합에서 공유하는 여러 전역 모듈을 사용하여 좀 더 엄격하게 제어 할 수도 있습니다. 내 전역 모듈에 짧은 이름을 사용하는 이유는 코드가 너무 복잡해지지 않도록하기 위해서입니다. 약간의 경험만으로도 1 ~ 2 개의 캐릭터만으로도 충분히 니모닉이됩니다.

x가 g에 이미 정의되지 않았을 때 gx에 할당하는 것은 여전히 ​​가능하며 다른 모듈이 gx에 액세스 할 수 있습니다. 그러나 인터프리터가 허용하더라도이 접근 방식은 투명하지 않습니다. 그것. 할당에 대한 변수 이름의 오타로 인해 g에 실수로 새 변수를 만들 가능성이 여전히 있습니다. 때때로 dir (g) 검사는 그러한 사고로 인해 생긴 놀라운 이름을 발견하는 데 유용합니다.


7
이 흥미로운 관찰은 내 문제를 해결했습니다. '나는 가져 오기시에만 g에서 초기화되는 로컬 변수를 생성하기 때문에 "from g import var"를 사용하지 않습니다.' "from..import"가 "import"와 동일하다고 가정하는 것이 타당 해 보이지만 사실이 아닙니다.
Curtis Yallop 2014 년

24

모듈을 정의하고 ( "globalbaz"라고 함) 그 안에 변수를 정의하십시오. 이 "의사 전역"을 사용하는 모든 모듈은 "globalbaz"모듈을 가져 와서 "globalbaz.var_name"을 사용하여 참조해야합니다.

이것은 변경 위치에 관계없이 작동하며 가져 오기 전후에 변수를 변경할 수 있습니다. 가져온 모듈은 최신 값을 사용합니다. (나는 이것을 장난감 예제에서 테스트했습니다)

설명을 위해 globalbaz.py는 다음과 같습니다.

var_name = "my_useful_string"

9

한 모듈의 전역을 다른 모듈로 전달할 수 있습니다.

모듈 A에서 :

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

모듈 B :

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3

7

전역 변수는 일반적으로 나쁜 생각이지만 다음을 지정하여 수행 할 수 있습니다 __builtins__.

__builtins__.foo = 'something'
print foo

또한 모듈 자체는 모든 모듈에서 액세스 할 수있는 변수입니다. 따라서 다음과 같은 모듈을 정의하면 my_globals.py:

# my_globals.py
foo = 'something'

그런 다음 어디서나 사용할 수 있습니다.

import my_globals
print my_globals.foo

수정보다는 모듈을 사용 __builtins__하는 것이 일반적으로 이런 종류의 전역을 수행하는 더 깨끗한 방법입니다.


3
__builtins__의 CPython 특색은, 당신이 정말로 그것을 사용하지 말아야 -보다 효율적으로 사용 __builtin__(또는 builtins같은 Python3에서) 허용 대답의
토비아스 Kienzler

5

모듈 수준 변수로 이미이 작업을 수행 할 수 있습니다. 모듈은 가져 오는 모듈에 관계없이 동일합니다. 따라서 어떤 모듈에서든 변수를 모듈 수준의 변수로 만들고 다른 모듈에서 액세스하거나 할당 할 수 있습니다. 변수의 값을 설정하거나 단일 객체의 속성으로 만드는 함수를 호출하는 것이 좋습니다. 이렇게하면 변수가 변경 될 때 일부 코드를 실행해야하는 경우 모듈의 외부 인터페이스를 손상시키지 않고 실행할 수 있습니다.

일반적으로 작업을 수행하는 좋은 방법은 아닙니다. 전역을 사용하는 경우는 드뭅니다.하지만 이것이 가장 깨끗한 방법이라고 생각합니다.


3

변수를 찾을 수없는 경우가 있다는 답변을 게시하고 싶었습니다.

주기적 가져 오기는 모듈 동작을 손상시킬 수 있습니다.

예를 들면 :

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

이 예제에서는 분명해야하지만 큰 코드 기반에서는 정말 혼란 스러울 수 있습니다.


1

이것은 __builtin__이름 공간을 수정하는 것처럼 들립니다 . 그렇게하려면 :

import __builtin__
__builtin__.foo = 'some-value'

__builtins__직접 사용하지 마십시오 (추가 "s"에 유의하십시오). 분명히 이것은 사전이나 모듈 일 수 있습니다. 이것을 지적한 ΤΖΩΤΖΙΟΥ에게 감사드립니다 . 여기 에서 더 많은 것을 찾을 수 있습니다 .

이제 foo어디에서나 사용할 수 있습니다.

나는 이것을 일반적으로 권장하지 않지만 이것을 사용하는 것은 프로그래머에게 달려 있습니다.

할당은 위와 같이해야하며 설정 만하면 foo = 'some-other-value'현재 네임 스페이스에서만 설정됩니다.


1
(comp.lang.python에서) 직접 내장 을 사용하는 것은 피해야 한다는 것을 기억 합니다. 대신 Curt Hagenlocher가 제안한대로 빌트인을 가져 와서 사용하십시오.
tzot

1

필자는 실제로 누락되었다고 느꼈던 몇 가지 기본 제공 기본 함수에 이것을 사용합니다. 한 가지 예는 filter, map, reduce와 동일한 사용 의미를 갖는 찾기 함수입니다.

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

일단 이것이 실행되면 (예를 들어, 진입 점 근처에서 가져옴으로써) 모든 모듈은 마치 내장 된 것처럼 find ()를 사용할 수 있습니다.

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

참고 : 물론 필터와 다른 라인을 사용하여 길이가 0인지 테스트하거나 한 종류의 이상한 라인에서 감소를 사용하여이 작업을 수행 할 수 있지만 항상 이상하다고 느꼈습니다.


1

사전을 사용하여 모듈 간 수정 가능 (또는 변경 가능 ) 변수를 얻을 수 있습니다 .

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

를 시작할 때 test_wait_app_up_fail실제 시간 초과 기간은 3 초입니다.


1

변수 값을 전달하기 위해 global / module 네임 스페이스 대신 클래스 네임 스페이스 를 사용하여 전역 변수 (예 : http://wiki.c2.com/?GlobalVariablesAreBad 참조 )를 사용하는 단점을 피할 수 있는지 궁금했습니다. . 다음 코드는 두 메서드가 본질적으로 동일 함을 나타냅니다. 아래에 설명 된대로 클래스 네임 스페이스를 사용하면 약간의 이점이 있습니다.

다음 코드 조각은 또한 전역 / 모듈 네임 스페이스와 클래스 네임 스페이스 모두에서 특성 또는 변수가 동적으로 생성 및 삭제 될 수 있음을 보여줍니다.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

이 모듈은 변수를 바운스하는 데 사용되기 때문에 '벽'이라고 부릅니다. 빈 클래스 '라우터'의 전역 변수 및 클래스 전체 속성을 임시로 정의하는 공간 역할을합니다.

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

이 모듈은 wall을 가져 sourcefn와서 메시지를 정의하고 하나는 전역을 통해 하나는 라우터 기능을 통해 두 개의 서로 다른 메커니즘으로 메시지를 방출 하는 단일 기능 을 정의합니다. wall.msg및 변수 wall.router.message는 각각의 네임 스페이스에서 처음으로 여기에서 정의됩니다.

dest.py

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

이 모듈은 destfn소스에서 방출 된 메시지를 수신하기 위해 두 가지 다른 메커니즘을 사용 하는 함수 를 정의합니다 . 변수 'msg'가 존재하지 않을 가능성을 허용합니다.destfn또한 표시되면 변수를 삭제합니다.

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

이 모듈은 이전에 정의 된 함수를 순서대로 호출합니다. 에 대한 첫 번째 호출 후 dest.destfn변수 wall.msgwall.router.msg더 이상 존재한다.

프로그램의 출력은 다음과 같습니다.

글로벌 : 안녕하세요!
라우터 : 안녕하세요!
글로벌 : 메시지 없음
라우터 : 메시지 없음

위의 코드 조각은 모듈 / 글로벌 및 클래스 / 클래스 변수 메커니즘이 본질적으로 동일 함을 보여줍니다.

많은 변수를 공유해야하는 경우 여러 벽 유형 모듈 (예 : wall1, wall2 등)을 사용하거나 여러 라우터 유형 클래스를 단일 파일에 정의하여 네임 스페이스 오염을 관리 할 수 ​​있습니다. 후자는 약간 더 깔끔하므로 클래스 변수 메커니즘을 사용하는 데있어 약간의 이점을 나타낼 수 있습니다.

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