Napotkałem trochę ściany importującej moduły w skrypcie Pythona. Zrobię co w mojej mocy, aby opisać błąd, dlaczego na niego wpadam i dlaczego wiążę to szczególne podejście, aby rozwiązać mój problem (który opiszę za chwilę):
Załóżmy, że mam moduł, w którym zdefiniowałem kilka funkcji / klas narzędzi, które odnoszą się do jednostek zdefiniowanych w przestrzeni nazw, do której ten moduł pomocniczy zostanie zaimportowany (niech „a” będzie taką jednostką):
moduł 1:
def f():
print a
Następnie mam program główny, w którym zdefiniowano „a”, do którego chcę zaimportować te narzędzia:
import module1
a=3
module1.f()
Uruchomienie programu wywoła następujący błąd:
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
Podobne pytania zadawano już w przeszłości (dwa dni temu, d'uh) i zasugerowano kilka rozwiązań, jednak nie sądzę, aby pasowały one do moich wymagań. Oto mój szczególny kontekst:
Próbuję stworzyć program w języku Python, który łączy się z serwerem bazy danych MySQL i wyświetla / modyfikuje dane za pomocą GUI. Ze względu na czystość zdefiniowałem kilka pomocniczych / narzędziowych funkcji związanych z MySQL w osobnym pliku. Jednak wszystkie mają wspólną zmienną, którą pierwotnie zdefiniowałem w module narzędzi, a która jest obiektem kursora z modułu MySQLdb. Później zdałem sobie sprawę, że obiekt kursora (który jest używany do komunikacji z serwerem db) powinien być zdefiniowany w module głównym, tak aby zarówno moduł główny, jak i wszystko, co jest do niego importowane, miało dostęp do tego obiektu.
Wynik końcowy wyglądałby mniej więcej tak:
utilities_module.py:
def utility_1(args):
code which references a variable named "cur"
def utility_n(args):
etcetera
I mój główny moduł:
program.py:
import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *
A potem, gdy tylko spróbuję wywołać którąkolwiek z funkcji narzędziowych, wyzwala wspomniany wyżej błąd „nie zdefiniowano nazwy globalnej”.
Szczególną sugestią było umieszczenie w pliku narzędzi instrukcji „from program import import cur”, na przykład:
utilities_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 *
Ale to cykliczny import lub coś w tym rodzaju i, podsumowanie, też się zawiesza. Więc moje pytanie brzmi:
Jak u diabła mogę sprawić, by obiekt "cur", zdefiniowany w głównym module, był widoczny dla tych funkcji pomocniczych, które są do niego importowane?
Dziękuję za poświęcony czas i najgłębsze przeprosiny, jeśli rozwiązanie zostało opublikowane w innym miejscu. Po prostu nie mogę sam znaleźć odpowiedzi i nie mam więcej sztuczek w mojej książce.
fetch_all
przechodzić przez dwie listy) , lub po prostu możesz mieć dwa różne wątki / greenlety / łańcuchy wywołań zwrotnych / cokolwiek korzystającego z bazy danych bez konfliktów).db
(icur
, jeśli nalegasz) w osobnym module, że zarównoprogram
iutilities_module
zaimportować go z. W ten sposób nie otrzymujesz zależności cyklicznych (importowanie programu z modułów, które program importuje) i zamieszania, które się z nimi wiążą.Odpowiedzi:
Globale w Pythonie są globalne dla modułu , a nie dla wszystkich modułów. (Wiele osób jest tym zdezorientowanych, ponieważ, powiedzmy, C, globalny jest taki sam we wszystkich plikach implementacji, chyba że wyraźnie to zrobisz
static
.)Istnieją różne sposoby rozwiązania tego problemu, w zależności od rzeczywistego przypadku użycia.
Zanim jeszcze pójdziesz tą ścieżką, zadaj sobie pytanie, czy to naprawdę musi być globalne. Może naprawdę potrzebujesz klasy z
f
metodą instancji, a nie tylko darmowej funkcji? Wtedy możesz zrobić coś takiego:Jeśli naprawdę chcesz globalnego, ale jest tylko po to, by go używać
module1
, ustaw go w tym module.Z drugiej strony, jeśli
a
jest współużytkowany przez wiele modułów, umieść go w innym miejscu i niech wszyscy go zaimportują:… Oraz w module1.py:
Nie używaj
from
importu, chyba że zmienna ma być stałą.from shared_stuff import a
utworzy nowąa
zmienną zainicjowaną do tego, do czegoshared_stuff.a
odwoływano się w czasie importu, aa
przypisania do nie będą miały wpływu na tę nową zmiennąshared_stuff.a
.Lub, w rzadkich przypadkach, gdy naprawdę potrzebujesz, aby był naprawdę globalny wszędzie, jak wbudowany, dodaj go do wbudowanego modułu. Dokładne szczegóły różnią się między Pythonem 2.xi 3.x. W 3.x działa to tak:
źródło
Aby obejść ten problem, możesz rozważyć ustawienie zmiennych środowiskowych w warstwie zewnętrznej, w ten sposób.
main.py:
mymodule.py:
Jako dodatkowe zabezpieczenie, zajmij się przypadkiem, gdy MYVAL nie jest zdefiniowany w module.
źródło
Funkcja korzysta z wartości globalnych modułu, w którym jest zdefiniowana . Zamiast ustawiać
a = 3
, na przykład, powinieneś ustawićmodule1.a = 3
. Jeśli więc chcesz, aby byłycur
dostępne jako globalneutilities_module
, ustawutilities_module.cur
.Lepsze rozwiązanie: nie używaj globali. Przekaż potrzebne zmienne do funkcji, które ich potrzebują, lub utwórz klasę, aby spakować wszystkie dane razem i przekaż ją podczas inicjowania instancji.
źródło
Ten post jest tylko obserwacją zachowania Pythona, z którym się spotkałem. Może porady, które przeczytałeś powyżej, nie działają dla Ciebie, jeśli zrobiłeś to samo, co poniżej.
Mianowicie mam moduł, który zawiera zmienne globalne / współdzielone (jak zasugerowano powyżej):
Następnie miałem główny moduł, który importuje udostępnione rzeczy z:
i kilka innych modułów, które faktycznie zapełniły te tablice. Są one wywoływane przez główny moduł. Wychodząc z tych innych modułów, wyraźnie widzę, że tablice są zapełnione. Ale kiedy czytałem je z powrotem w głównym module, były puste. To było dla mnie dość dziwne (no cóż, jestem nowy w Pythonie). Jednak gdy zmienię sposób importu sharedstuff.py w głównym module na:
zadziałało (tablice zostały wypełnione).
Tylko mówię'
źródło
Najłatwiejszym rozwiązaniem tego konkretnego problemu byłoby dodanie innej funkcji w module, która przechowywałaby kursor w zmiennej globalnej modułu. Wtedy wszystkie inne funkcje również mogłyby go używać.
moduł 1:
główny program:
źródło
Ponieważ wartości globalne są specyficzne dla modułu, możesz dodać następującą funkcję do wszystkich zaimportowanych modułów, a następnie użyć jej do:
W takim razie wszystko, co musisz przekazać bieżącym globalom, to:
źródło
Ponieważ nie widziałem tego w powyższych odpowiedziach, pomyślałem, że dodam moje proste obejście, którym jest po prostu dodanie
global_dict
argumentu do funkcji wymagającej wartości globalnych modułu wywołującego, a następnie przekazanie dyktu do funkcji podczas wywoływania; na przykład:źródło
Sposobem OOP na zrobienie tego byłoby uczynienie modułu klasą zamiast zestawu niezwiązanych metod. Następnie możesz użyć
__init__
metody lub metody ustawiającej, aby ustawić zmienne z obiektu wywołującego do użycia w metodach modułu.źródło