Używasz zmiennych globalnych między plikami?

207

Jestem trochę zdezorientowany, jak działają zmienne globalne. Mam duży projekt z około 50 plikami i muszę zdefiniować zmienne globalne dla wszystkich tych plików.

To, co zrobiłem, to zdefiniowanie ich w main.pypliku moich projektów , jako:

# ../myproject/main.py

# Define global myList
global myList
myList = []

# Imports
import subfile

# Do something
subfile.stuff()
print(myList[0])

Próbuję użyć myListw subfile.pynastępujący sposób

# ../myproject/subfile.py

# Save "hey" into myList
def stuff():
    globals()["myList"].append("hey")

Próbowałem w inny sposób, ale też nie działałem

# ../myproject/main.py

# Import globfile    
import globfile

# Save myList into globfile
globfile.myList = []

# Import subfile
import subfile

# Do something
subfile.stuff()
print(globfile.myList[0])

A w środku subfile.pymiałem to:

# ../myproject/subfile.py

# Import globfile
import globfile

# Save "hey" into myList
def stuff():
    globfile.myList.append("hey")

Ale znowu to nie zadziałało. Jak mam to zaimplementować? Rozumiem, że to nie może tak działać, gdy dwa pliki tak naprawdę się nie znają (dobrze, podtekst nie zna głównego), ale nie mogę wymyślić, jak to zrobić, bez użycia pisma io marynowaniu, które Nie chcę robić

martineau
źródło
W rzeczywistości twoje drugie podejście jest dla mnie w porządku. main.py poprawnie wypisuje „hej”. Czy możesz bardziej precyzyjnie określić, na czym polega „nie działało”?
rodion
@rodion: importowanie cykli - kod w podpliku próbuje zaimportować plik globalny, który w nim sam importuje się z powrotem
jsbueno
1
NameError: name 'myList' is not definedz main.pyliniiprint(globfile.myList[0])

Odpowiedzi:

318

Problem polega na zdefiniowaniu myListz main.py, ale subfile.pymusi go używać. Oto czysty sposób na rozwiązanie tego problemu: przenieś wszystkie globale do pliku, nazywam ten plik settings.py. Ten plik jest odpowiedzialny za zdefiniowanie globalizacji i ich zainicjowanie:

# settings.py

def init():
    global myList
    myList = []

Następnie subfilemożesz zaimportować globale:

# subfile.py

import settings

def stuff():
    settings.myList.append('hey')

Zauważ, że subfilenie dzwoni init()- to zadanie należy do main.py:

# main.py

import settings
import subfile

settings.init()          # Call only once
subfile.stuff()         # Do stuff with global var
print settings.myList[0] # Check the result

W ten sposób osiągasz swój cel, unikając inicjalizacji zmiennych globalnych więcej niż jeden raz.

Hai Vu
źródło
40
Podoba mi się ogólne podejście, ale nie całość init(). Moduły są oceniane tylko przy pierwszym imporcie, więc inicjowanie tych zmiennych w treści modułu jest całkowicie w porządku.
Kirk Strauser
19
+1 Kirk: Zgadzam się. Jednak moje podejście zapobiega przypadkowi, w którym inne moduły modyfikują globals.myList przed uruchomieniem głównego programu.
Hai Vu
2
Powinieneś nazwać to czymś innym niż globals, co jest wbudowaną nazwą. PyLint ostrzega: „Przedefiniowanie wbudowanych„ globałów ”(przedefiniowanie wbudowane)”
twasbrillig
Dzięki. Masz pomysł, jak usunąć błędy „Niezdefiniowana zmienna z importu” pojawiające się w Eclipse PyDev przy użyciu tej struktury plików (tj. Importując zmienne globalne z settings.py)? Musiałem wyłączyć błąd w PyDev , co nie jest idealne.
Franck Dernoncourt,
@FranckDernoncourt Przykro mi, nie używam Eclipse, więc jestem jeszcze bardziej nieświadomy niż ty.
Hai Vu,
93

Zobacz dokument Pythona na temat udostępniania zmiennych globalnych między modułami :

Kanonicznym sposobem udostępniania informacji między modułami w ramach jednego programu jest utworzenie specjalnego modułu (często nazywanego config lub cfg).

config.py:

x = 0   # Default value of the 'x' configuration setting

Zaimportuj moduł konfiguracji do wszystkich modułów aplikacji; moduł staje się wtedy dostępny jako nazwa globalna.

main.py:

import config
print (config.x)

lub

from config import x
print (x)

Ogólnie nie używaj z importu modulename * . Takie postępowanie zaśmieca przestrzeń nazw importera i znacznie utrudnia lintersom wykrywanie nieokreślonych nazw.

Ogaga Uzoh
źródło
1
Ratownik - tylko dla tego linku!
toonarmycaptain
4
To wydaje się czystsze podejście niż zaakceptowana odpowiedź.
JoeyC
2
Zauważ, że nie możesz ustawić xza pomocą from config import x, tylko używającimport config
Yariv
1
Najprostsze rozwiązanie! Dzięki
użytkownik1297406
22

Możesz myśleć o zmiennych globalnych w Pythonie jako zmiennych „modułowych” - i jako takie są one znacznie bardziej przydatne niż tradycyjne „zmienne globalne” z C.

Zmienna globalna jest faktycznie zdefiniowana w module __dict__i może być dostępna z zewnątrz tego modułu jako atrybut modułu.

W twoim przykładzie:

# ../myproject/main.py

# Define global myList
# global myList  - there is no "global" declaration at module level. Just inside
# function and methods
myList = []

# Imports
import subfile

# Do something
subfile.stuff()
print(myList[0])

I:

# ../myproject/subfile.py

# Save "hey" into myList
def stuff():
     # You have to make the module main available for the 
     # code here.
     # Placing the import inside the function body will
     # usually avoid import cycles - 
     # unless you happen to call this function from 
     # either main or subfile's body (i.e. not from inside a function or method)
     import main
     main.mylist.append("hey")
jsbueno
źródło
1
wow, normalnie można się spodziewać, że dwa pliki importujące się nawzajem wejdą w nieskończoną pętlę.
Nikhil VJ
2
ha Na pierwszy rzut oka tak to wygląda, prawda? To, co dzieje się w def stuff (), polega na tym, że import nie działa, gdy plik się ładuje .. działa tylko, gdy wywoływana jest funkcja stuff (). Więc zaczynając od main, importujemy podtekst, a następnie wywołujemy subfile.stuff (), który następnie importuje main ... no loop, po prostu importuj raz w main. Zobacz uwagę w przykładzie subfile.py na temat cykli importu.
Jan
11

Korzystanie from your_file import *powinno naprawić twoje problemy. Definiuje wszystko, aby było globalnie dostępne (z wyjątkiem zmiennych lokalnych podczas importu, oczywiście).

na przykład:

##test.py:

from pytest import *

print hello_world

i:

##pytest.py

hello_world="hello world!"
IT Ninja
źródło
4
Z wyjątkiem przypadku przypisania do jednej takiej zmiennej
jsbueno,
5
Osobiście unikam używania import *za wszelką cenę, aby odniesienia były jawne (i nie mylące). Poza tym, kiedy kiedykolwiek używałeś wszystkich *odniesień w jakimkolwiek module?
ThorSummoner,
19
NIE importuj *. Twoje zmienne globalne nie będą już zsynchronizowane. Każdy moduł otrzymuje własną kopię. Zmiana zmiennej w jednym pliku nie będzie odzwierciedlona w innym. Jest to również przestrzegane w docs.python.org/2/faq/…
Isa Hassen
8

Odpowiedź Hai Vu działa świetnie, tylko jeden komentarz:

Jeśli używasz globalnego w innym module i chcesz dynamicznie ustawiać globalny, zwróć uwagę na import innych modułów po ustawieniu globalnych zmiennych, na przykład:

# settings.py
def init(arg):
    global myList
    myList = []
    mylist.append(arg)


# subfile.py
import settings

def print():
    settings.myList[0]


# main.py
import settings
settings.init("1st")     # global init before used in other imported modules
                         # Or else they will be undefined

import subfile    
subfile.print()          # global usage
Lastboy
źródło
4

Druga próba będzie działać idealnie i jest to naprawdę dobry sposób na obsługę nazw zmiennych, które chcesz mieć dostępne na całym świecie. Ale masz błąd nazwy w ostatnim wierszu. Oto jak powinno być:

# ../myproject/main.py

# Import globfile    
import globfile

# Save myList into globfile
globfile.myList = []

# Import subfile
import subfile

# Do something
subfile.stuff()
print(globfile.myList[0])

Widzisz ostatnią linię? myList to atrybut pliku globalnego, a nie podtekstu. To zadziała tak, jak chcesz.

Mikrofon

MikeHunter
źródło