Nie jestem pewien, skąd wzięło się takie przeświadczenie, ale ostatnio kilka osób próbowało mnie przekonać, że w Pythonie nie da się wykonać dwukierunkowego importu.
Rozważmy naiwny przykład programu ładującego wtyczkę, która z kolei chce korzystać z API swojego hosta. Zgadzam się, że takie dwukierunkowe zależności to na ogół oznaka bardzo złego designu, są jednak sytuacje, w których ciężko ich uniknąć.
Pozostaje problem implementacji. Okazuje się, że zwolennicy teorii nie da się
dzielą się na tych, którzy nigdy nie próbowali, ale ktoś coś wspominał
oraz na tych, którzy oczekiwali od Pythona zdolności myślenia. Dwukierunkowy import na poziomie globalnym modułów nie wymaga specjalnych zabiegów, trzeba być jednak przygotowanym na dwa ograniczenia:
- Importować można tylko wstecz, a więc wyłącznie symbole zdefiniowane przed importem, który doprowadził do powstania pętli (co jest dość logiczne, gdyż w pozostałych przypadkach mamy do czynienia z problemem jajka i kury)
- W pewnych okolicznościach kod zostanie wykonany dwukrotnie
Działającą pętlę importów pokazuje poniższy przykład:
Uwaga: na diagramie jest literówka, oczywiście moduł bar powinien importować symbol a zamiast b.



by jarek
22 lip 2009 at 15:43
Dla mnie zmorą są importy zaszyte np. w środku (pół biedy gdy są zaraz obok definicji) metody/funkcji.
Czasami ciężko się połapać w takich potworkach jeśli są nieudokumentowane przez co wdrażanie/testowanie aplikacji jest o wiele bardziej upierdliwe nie mówiąc o pracy z takim kodem.
by japhy
22 lip 2009 at 16:00
Eee… a czy w trzeciej linijce bar.py nie powinno być przypadkiem „import c” albo „import a”? Nie do końca rozumiem, jak to miałoby działać — czy jeśli importowana z pętli zmienna jest zdefiniowana (przypisana) powyżej, a następnie zredefiniowana *poniżej* importu tworzącego pętlę — czy z pętli dostaniemy pierwszą wartość, czy błąd? Co jeśli zamiast redefinicji mamy zmianę mutowalnego stanu?
Wydaje mi się, że częściowe wsparcie dla pętli importów to więcej zamętu niż korzyści. Bo i właściwie po co?
by Patrys
22 lip 2009 at 16:12
japhy:
Powinno być „a”, literówka.
Dlaczego „częściowe” wsparcie? Jest kompletne. Po co? Choćby Haystack — pierwsza z brzegu aplikacja Django, która importuje pozostałe aplikacje projektu, zbierając modele ORM do indeksowania. Z kolei twój kod chciałby pewnie móc coś w tym ideksie wyszukać, co wymaga zaimportowania Haystacka.
by japhy
22 lip 2009 at 19:57
Jest częściowe, bo kompletnego nie bardzo jest jak zaimplementować (nie zrobisz „from foo import *”, a nawet „import foo” w bar.py, prawda?).
A Haystacka albo importuję w models.py i sam wołam haystack.site.register() — albo:
1. w urls.py projektu importuję haystack
2. wołam haystack.autodiscover()
3. autodiscover importuje search_indexes.py w tych aplikacjach, w których jest dostępne
4. search_indexes.py może spokojnie zaimportować haystack — już został wcześniej zaimportowany przez urls.py, moduł jest już w pamięci.
Discovery nie leci w czasie importowania haystacka — jest wykonywane później. Więc pudło.
by Patrys
22 lip 2009 at 21:00
japhy:
Oczywiście, że w jedną stronę możesz zażyczyć sobie
import fooalbo ifrom foo import *. Sprawdzenie tego nie zajmie ci więcej czasu niż napisanie komentarza.W cale nie pudło. Ostatnio problem w biurze był właśnie z aplikacją third-party (nazwy nie podam), która do spółki z Haystackiem tworzyła pętlę zależności (Haystack wymusza wczesne załadowanie
urls, sprawdź w kodzie; obie aplikacje robiły swoje autodiscover metodą brute-force).by japhy
22 lip 2009 at 22:34
Zabawne, i „print foo.c” w bar.py rzuca AttributeError, a nie błąd w imporcie — czyli dostajemy częściowo załadowany moduł; cel pewnie jest taki, żeby móc się do całości modułu odwołać już w funkcji czy metodzie, wtedy nazwa rozwijana jest dopiero po załadowaniu całego foo — ale fajnie musi się coś takiego debugować ;)
O Haystacku pisałem na podstawie jego dokumentacji, jeszcze go nie używałem; może bardziej zaawansowane wykorzystanie robi pętle — ale to IMO więcej kłopotu niż to warte (właśnie ze względu na zabawę z debugowaniem jeśli jednak coś pójdzie inaczej, niż myślimy — a kiedyś pójdzie).