2010-06-21 17 views
13

Jestem nowicjuszem Pythona pochodzącym z tła C++. Chociaż wiem, że to nie jest Pythonic, aby spróbować znaleźć pasującą koncepcję przy użyciu mojej starej wiedzy z C++, myślę, że to pytanie jest nadal ogólne pytanie:Zamówienie inicjalizacji modułu Pythona?

Pod C++, istnieje znany problem zwany inicjalizacją zmiennych globalnych/statycznych zamówienie fiasko, ze względu na niezdolność C++ do decydowania o tym, która zmienna globalna/statyczna zostanie zainicjowana najpierw w jednostkach kompilacji, w związku z czym zmienna globalna/statyczna w zależności od innej w różnych jednostkach kompilacji może zostać zainicjowana wcześniej niż jej odpowiedniki zależne, a gdy zależność zaczęła korzystać z usług dostarczanych przez obiekt zależności, mielibyśmy niezdefiniowane zachowanie. Tutaj nie chcę zbytnio zagłębiać się w to, jak C++ rozwiązuje ten problem. :)

W świecie Pythona widzę zastosowania zmiennych globalnych, nawet w różnych plikach .py, a jeden przypadek użycia typycal, który widziałem: zainicjować jeden obiekt globalny w jednym pliku .py, a na innym .py plików, kod po prostu odważnie zaczyna używać obiektu globalnego, zakładając, że musi on zostać zainicjowany gdzie indziej, co w C++ jest zdecydowanie nieakceptowane przeze mnie z powodu problemu, który opisałem powyżej.

Nie jestem pewien, czy powyższy przypadek użycia jest powszechną praktyką w Pythonie (Python) i jak Python rozwiązuje ogólnie ten rodzaj problemu z porządkiem inicjowania zmiennej globalnej?

Dziękuję bardzo!

Lin

Odpowiedz

10

Zgodnie C++, jest dobrze znany problem zwany globalny/statyczne zmienne inicjalizacji kolejność f iasco, ze względu na niemożność C++ 's do decydowania o zmiennej globalny/statyczne byłby pierwszy zainicjowany we wszystkich jednostkach kompilacji,

myślę, że sprawozdanie podkreśla kluczową różnicę między Python i C++: w Pythonie, nie ma czegoś takiego jak inna jednostki kompilacyjne. Mam na myśli to, że w C++ (jak wiadomo) dwa różne pliki źródłowe mogą być kompilowane całkowicie niezależnie od siebie, a zatem jeśli porównasz wiersz w pliku A i wiersz w pliku B, nie ma nic do powiedzenia ty, który zostanie umieszczony jako pierwszy w programie. To trochę tak, jak w przypadku wielu wątków: nie można powiedzieć, czy konkretna instrukcja w wątku 1 zostanie wykonana przed czy po określonej instrukcji w wątku 2. Można powiedzieć, że programy w języku C++ są kompilowane równolegle.

W przeciwieństwie do tego w języku Python wykonywanie rozpoczyna się od początku jednego pliku i przebiega w ściśle określonej kolejności przez każdą instrukcję w pliku, rozgałęziając się do innych plików w punktach, w których są importowane.Tak naprawdę można by pomyśleć o dyrektywie import jako #include iw ten sposób można zidentyfikować kolejność wykonywania wszystkich linii kodu we wszystkich plikach źródłowych w programie. (Cóż, jest to trochę bardziej skomplikowane, ponieważ moduł jest naprawdę uruchamiany dopiero przy pierwszym importowaniu iz innych powodów.) Jeśli programy w C++ są kompilowane równolegle, programy Python są interpretowane szeregowo.

Twoje pytanie dotyczy także głębszego znaczenia modułów w Pythonie. Moduł Pythona - czyli wszystko, co znajduje się w jednym pliku .py - jest rzeczywistym obiektem. Wszystko zadeklarowane w "globalnym" zasięgu w jednym pliku źródłowym jest w rzeczywistości atrybutem tego obiektu modułu. W Pythonie nie ma prawdziwego globalnego zasięgu. (Programiści Python często mówią "globalny" i faktycznie jest w tym języku słowo kluczowe global, ale zawsze odnosi się ono do najwyższego poziomu bieżącego modułu). Widziałem, że jest to trochę dziwna koncepcja, żeby się przyzwyczaić pochodzący z tła C++. Zajęło mi to trochę przyzwyczajenie się do mnie, pochodząc z Javy, a pod tym względem Java jest dużo bardziej podobna do Pythona niż C++. (Nie ma też globalnego zasięgu w Javie)

Wspomnę, że w Pythonie zupełnie normalne jest używanie zmiennej bez żadnego pojęcia, czy została ona zainicjowana/zdefiniowana czy nie. Cóż, może nie normalne, ale przynajmniej dopuszczalne w odpowiednich okolicznościach. W języku Python próba użycia niezdefiniowanej zmiennej powoduje zwiększenie wartości o NameError; nie zachowujesz się arbitralnie, jak w C lub C++, dzięki czemu łatwo poradzisz sobie z sytuacją. Można zobaczyć ten wzór:

try: 
    duck.quack() 
except NameError: 
    pass 

który nic nie robi, jeśli duck nie istnieje. Właściwie, co można zobaczyć częściej jest

try: 
    duck.quack() 
except AttributeError: 
    pass 

który nie robi nic, jeśli duck nie posiada metodę o nazwie quack. (AttributeError jest rodzajem błędu, który pojawia się przy próbie uzyskania dostępu do atrybutu obiektu, ale obiekt nie ma żadnego atrybutu o tej nazwie.) To jest to, co przechodzi dla sprawdzenia typu w Pythonie: uważamy, że jeśli wszystko do zrobienia wystarczy kaczka, możemy po prostu poprosić ją o szarlatan, a jeśli tak, to nie obchodzi nas, czy to kaczka, czy nie. (Nazywa się to kaczym pisaniem ;-)

+0

To zdecydowanie dokładne wyjaśnienie problemu, który napisałem, dokładnie do rzeczy! Dzięki! – Lin

12

Python import uruchamia nowe moduły Pythona od początku do końca. Kolejny import powoduje tylko kopię istniejącego odwołania w sys.modules, nawet jeśli nadal znajduje się w trakcie importowania modułu z powodu importu kołowego. Atrybuty modułów ("zmienne globalne" znajdują się w zasięgu modułu), które zostały zainicjowane przed zaimportowaniem cyklicznym.

main.py:

import a 

a.py:

var1 = 'foo' 
import b 
var2 = 'bar' 

b.py:

import a 
print a.var1 # works 
print a.var2 # fails 
+0

Kolejność inicjowania zmiennych globalnych Pythona jest przeprowadzana i wymuszana jawnie za pomocą instrukcji "import", co ma sens, ponieważ C++ nie ma możliwości wyraźnego określenia relacji zależności jednostek kompilacji. Dziękuję za wyjaśnienie! – Lin

Powiązane problemy