Jak utworzyć zmienną międzymodułową?


122

__debug__Zmienna jest przydatny w części, ponieważ ma wpływ na każdy moduł. Jeśli chcę utworzyć inną zmienną, która działa w ten sam sposób, jak mam to zrobić?

Zmienna (bądźmy oryginalna i nazwijmy ją „foo”) nie musi być prawdziwie globalna, w tym sensie, że jeśli zmienię foo w jednym module, zostanie zaktualizowana w innych. Byłoby dobrze, gdybym mógł ustawić foo przed zaimportowaniem innych modułów, a wtedy zobaczyliby dla niego tę samą wartość.

Odpowiedzi:


114

Nie popieram tego rozwiązania w żaden sposób, w żadnym kształcie ani formie. Ale jeśli dodasz zmienną do __builtin__modułu, będzie ona dostępna tak, jakby była globalna z dowolnego innego modułu, który zawiera __builtin__- czyli domyślnie wszystkie.

a.py zawiera

print foo

b.py zawiera

import __builtin__
__builtin__.foo = 1
import a

W rezultacie drukowane jest „1”.

Edit:__builtin__ moduł jest dostępny jako lokalny symbol __builtins__- to jest powodem rozbieżności między dwóch z tych odpowiedzi. Zwróć również uwagę, że w python3 __builtin__zmieniono nazwę na builtins.


2
Jest jakiś powód, dla którego nie lubisz tej sytuacji?
Entuzjazm oprogramowania

31
Po pierwsze, łamie oczekiwania ludzi, gdy czytają kod. „Co to za symbol„ foo ”jest tutaj używany? Dlaczego nie mogę zobaczyć, gdzie jest zdefiniowany?”
Curt Hagenlocher

9
Jest również podatne na siejanie spustoszenia, jeśli przyszła wersja Pythona zacznie używać nazwy, którą wybrałeś jako rzeczywistą wbudowaną wersję.
intuicyjnie

4
Jest to dobre rozwiązanie do takich rzeczy, jak udostępnianie połączenia db z importowanymi modułami. Aby sprawdzić poprawność, upewniam się, że zaimportowany moduł potwierdza hasattr(__builtin__, "foo").
Mike Ellis

4
Dla każdego, kto czyta tę odpowiedź: NIE! ROBIĆ ! TO! Naprawdę nie.
bruno desthuilliers

161

Jeśli potrzebujesz globalnej zmiennej międzymodułowej, może wystarczy prosta globalna zmienna na poziomie modułu.

a.py:

var = 1

b.py:

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

c.py:

import a
a.var = 2

Test:

$ python b.py
# -> 1 2

Przykład ze świata rzeczywistego: plik global_settings.py Django (chociaż w aplikacjach Django ustawienia są używane przez importowanie obiektu django.conf.settings ).


3
Lepsze, ponieważ pozwala uniknąć możliwych konfliktów przestrzeni nazw
bgw

A jeśli moduł, który importujesz, w tym przypadku a.pyzawiera main()? Czy to ma znaczenie?
sedeh

4
@sedeh: no. Jeśli a.py jest również uruchamiany jako skrypt, użyj if __name__=="__main__"w nim guard, aby uniknąć uruchamiania nieoczekiwanego kodu podczas importu.
jfs

7
W prawdziwym świecie z takim rozwiązaniem trzeba uważać. Jeśli programista pobierze zmienną „globalną” za pomocą „z importowanej zmiennej” (spróbuj tej odmiany w c.py), otrzyma kopię zmiennej w czasie importu.
Paul Whipp

1
@PaulWhipp: źle (wskazówka: użyj, id()aby sprawdzić tożsamość)
jfs

25

Uważam, że istnieje wiele okoliczności, w których ma to sens i upraszcza programowanie, aby mieć pewne elementy globalne, które są znane w kilku (ściśle powiązanych) modułach. W tym duchu chciałbym nieco rozwinąć ideę posiadania modułu globali, który jest importowany przez te moduły, które muszą się do nich odnosić.

Gdy jest tylko jeden taki moduł, nazywam go „g”. W nim przypisuję wartości domyślne dla każdej zmiennej, którą zamierzam traktować jako globalną. W każdym module, który używa któregokolwiek z nich, nie używam "from g import var", ponieważ skutkuje to tylko zmienną lokalną, która jest inicjowana z g tylko w czasie importu. Większość odniesień tworzę w postaci g.var i „g”. służy jako stałe przypomnienie, że mam do czynienia ze zmienną, która jest potencjalnie dostępna dla innych modułów.

Jeśli wartość takiej zmiennej globalnej ma być często używana w jakiejś funkcji w module, to ta funkcja może wykonać lokalną kopię: var = g.var. Jednak ważne jest, aby zdać sobie sprawę, że przypisania do var są lokalne, a globalny g.var nie może być aktualizowany bez wyraźnego odniesienia do g.var w przypisaniu.

Zauważ, że możesz również mieć wiele takich modułów globalnych współdzielonych przez różne podzbiory modułów, aby zachować nieco ściślejszą kontrolę. Powodem, dla którego używam krótkich nazw dla moich modułów globalnych, jest uniknięcie zbytniego zaśmiecania kodu ich wystąpieniami. Mając tylko niewielkie doświadczenie, stają się wystarczająco mnemoniczne, mając tylko 1 lub 2 znaki.

Nadal jest możliwe przypisanie do, powiedzmy, gx, gdy x nie zostało już zdefiniowane w g, a inny moduł może wtedy uzyskać dostęp do gx Jednak mimo że interpreter na to zezwala, to podejście nie jest tak przejrzyste i unikam to. Nadal istnieje możliwość przypadkowego utworzenia nowej zmiennej w g w wyniku błędu w nazwie zmiennej dla przypisania. Czasami analiza dir (g) jest przydatna do odkrycia nazw niespodzianek, które mogły powstać w wyniku takiego wypadku.


7
Ta interesująca obserwacja rozwiązała mój problem: „Nie używam„ from g import var ”, ponieważ skutkuje to tylko zmienną lokalną, która jest inicjowana z g tylko w czasie importu”. Wydaje się rozsądne założyć, że „from..import” to to samo, co „import”, ale to nieprawda.
Curtis Yallop,

24

Zdefiniuj moduł (nazwij go „globalbaz”) i umieść w nim zdefiniowane zmienne. Wszystkie moduły korzystające z tego „pseudoglobalnego” powinny importować moduł „globalbaz” i odnosić się do niego przy użyciu „globalbaz.var_name”

Działa to niezależnie od miejsca zmiany, możesz zmienić zmienną przed lub po imporcie. Zaimportowany moduł użyje najnowszej wartości. (Przetestowałem to na przykładzie zabawki)

Dla wyjaśnienia, globalbaz.py wygląda tak:

var_name = "my_useful_string"

9

Możesz przekazać globale jednego modułu innemu:

W module A:

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

W module B:

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

7

Zmienne globalne to zwykle zły pomysł, ale możesz to zrobić, przypisując do __builtins__:

__builtins__.foo = 'something'
print foo

Ponadto same moduły są zmiennymi, do których można uzyskać dostęp z dowolnego modułu. Jeśli więc zdefiniujesz moduł o nazwie my_globals.py:

# my_globals.py
foo = 'something'

Następnie możesz użyć tego również z dowolnego miejsca:

import my_globals
print my_globals.foo

Używanie modułów zamiast modyfikowania __builtins__jest generalnie czystszym sposobem robienia tego typu obiektów globalnych.


3
__builtins__jest osobliwością CPythona, naprawdę nie powinieneś go używać - lepiej używać __builtin__(lub builtinsw Pythonie3), jak pokazuje akceptowana odpowiedź
Tobias Kienzler

5

Możesz to już zrobić za pomocą zmiennych na poziomie modułu. Moduły są takie same bez względu na to, z jakiego modułu są importowane. Możesz więc uczynić zmienną zmienną na poziomie modułu w dowolnym module, w którym ma sens, i uzyskać do niej dostęp lub przypisać do niej z innych modułów. Byłoby lepiej wywołać funkcję, aby ustawić wartość zmiennej lub uczynić ją właściwością jakiegoś pojedynczego obiektu. W ten sposób, jeśli w końcu będziesz musiał uruchomić jakiś kod po zmianie zmiennej, możesz to zrobić bez uszkadzania zewnętrznego interfejsu modułu.

Zwykle nie jest to świetny sposób robienia rzeczy - rzadko używa się globali - ale myślę, że jest to najczystszy sposób na zrobienie tego.


3

Chciałem zamieścić odpowiedź, że jest przypadek, w którym zmienna nie zostanie znaleziona.

Import cykliczny może zakłócić zachowanie modułu.

Na przykład:

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

W tym przykładzie powinno to być oczywiste, ale w przypadku dużej bazy kodu może to być naprawdę zagmatwane.


1

To brzmi jak modyfikacja __builtin__przestrzeni nazw. Aby to zrobić:

import __builtin__
__builtin__.foo = 'some-value'

Nie używaj __builtins__bezpośrednio (zwróć uwagę na dodatkowe „s”) - najwyraźniej może to być słownik lub moduł. Dzięki ΤΖΩΤΖΙΟΥ za wskazanie tego, więcej można znaleźć tutaj .

Teraz foojest dostępny do użytku wszędzie.

Generalnie nie polecam tego robić, ale użycie tego zależy od programisty.

Przypisanie do niego musi być wykonane jak powyżej, samo ustawienie foo = 'some-other-value'spowoduje ustawienie go tylko w bieżącej przestrzeni nazw.


1
Pamiętam (z comp.lang.python), że należy unikać używania bezpośrednio wbudowanych ; Zamiast tego importuj wbudowane i użyj tego, jak zasugerował Curt Hagenlocher.
tzot

1

Używam tego do kilku wbudowanych prymitywnych funkcji, których naprawdę brakowało. Jednym z przykładów jest funkcja wyszukiwania, która ma taką samą semantykę użycia jak filtr, mapowanie, redukcja.

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

import __builtin__
__builtin__.find = builtin_find

Po uruchomieniu tego (na przykład przez zaimportowanie w pobliżu punktu wejścia) wszystkie moduły mogą używać funkcji find (), tak jakby, oczywiście, została wbudowana.

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

Uwaga: możesz to oczywiście zrobić za pomocą filtra i innej linii, aby przetestować zerową długość lub z redukcją w jednym rodzaju dziwnej linii, ale zawsze uważałem, że to dziwne.


1

Mogłem uzyskać zmienne modyfikowalne (lub mutowalne ) między modułami za pomocą słownika:

# 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

Podczas uruchamiania test_wait_app_up_failrzeczywisty limit czasu wynosi 3 sekundy.


1

Zastanawiałem się, czy byłoby możliwe uniknięcie niektórych wad używania zmiennych globalnych (patrz np. Http://wiki.c2.com/?GlobalVariablesAreBad ) poprzez użycie przestrzeni nazw klas zamiast globalnej / modułowej przestrzeni nazw do przekazywania wartości zmiennych . Poniższy kod wskazuje, że te dwie metody są zasadniczo identyczne. Korzystanie z przestrzeni nazw klas ma pewną przewagę, jak wyjaśniono poniżej.

Poniższe fragmenty kodu pokazują również, że atrybuty lub zmienne mogą być dynamicznie tworzone i usuwane zarówno w globalnych / modułowych przestrzeniach nazw, jak iw przestrzeniach nazw klas.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

Nazywam ten moduł „ścianą”, ponieważ jest używany do odbijania zmiennych od. Będzie działać jako przestrzeń do tymczasowego definiowania zmiennych globalnych i atrybutów całej klasy pustej klasy „router”.

source.py

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

Ten moduł importuje ścianę i definiuje pojedynczą funkcję, sourcefnktóra definiuje wiadomość i emituje ją przez dwa różne mechanizmy, jeden poprzez globale, a drugi przez funkcję routera. Zwróć uwagę, że zmienne wall.msgi wall.router.messagesą zdefiniowane tutaj po raz pierwszy w odpowiednich przestrzeniach nazw.

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'

Ten moduł definiuje funkcję, destfnktóra wykorzystuje dwa różne mechanizmy do odbierania wiadomości emitowanych przez źródło. Uwzględnia możliwość, że zmienna „msg” może nie istnieć.destfnusuwa także zmienne po ich wyświetleniu.

main.py

import source, dest

source.sourcefn()

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

Ten moduł wywołuje po kolei wcześniej zdefiniowane funkcje. Po pierwszym wywołaniu dest.destfnzmiennych wall.msgi wall.router.msgjuż nie istnieją.

Dane wyjściowe z programu to:

global: Witaj, świecie!
router: Witaj, świecie!
global: brak wiadomości
router: brak wiadomości

Powyższe fragmenty kodu pokazują, że mechanizm moduł / globalny i mechanizm zmiennej klasy / klasy są zasadniczo identyczne.

Jeśli ma być współdzielonych wiele zmiennych, zanieczyszczeniem przestrzeni nazw można zarządzać za pomocą kilku modułów ściennych, np. Wall1, wall2 itp. Lub definiując kilka klas typu routera w jednym pliku. Ta ostatnia jest nieco uporządkowana, więc być może stanowi marginalną zaletę w stosowaniu mechanizmu zmiennych klas.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.