PEP 08 stwierdza:
Importy są zawsze umieszczane na początku pliku, tuż po komentarzach i ciągach dokumentów modułu, a przed globałami i stałymi modułu.
Jeśli jednak klasa / metoda / funkcja, którą importuję, jest używana tylko w rzadkich przypadkach, z pewnością bardziej efektywne jest importowanie, gdy jest potrzebne?
Czy to nie jest:
class SomeClass(object):
def not_often_called(self)
from datetime import datetime
self.datetime = datetime.now()
bardziej wydajny niż to?
from datetime import datetime
class SomeClass(object):
def not_often_called(self)
self.datetime = datetime.now()
python
optimization
coding-style
Adam J. Forster
źródło
źródło
Umieszczenie instrukcji importu w funkcji może zapobiec zależnościom kołowym. Na przykład, jeśli masz 2 moduły, X.py i Y.py, i oba muszą się importować, spowoduje to zależność cykliczną podczas importowania jednego z modułów, powodując nieskończoną pętlę. Jeśli przeniesiesz instrukcję importu do jednego z modułów, nie będzie ona próbowała zaimportować drugiego modułu, dopóki funkcja nie zostanie wywołana, a moduł ten zostanie już zaimportowany, więc nie będzie nieskończonej pętli. Przeczytaj tutaj więcej - effbot.org/zone/import-confusion.htm
źródło
Przyjąłem praktykę umieszczania wszystkich importów w funkcjach, które ich używają, a nie w górnej części modułu.
Korzyścią, którą otrzymuję, jest możliwość bardziej niezawodnego refaktoryzacji. Kiedy przenoszę funkcję z jednego modułu do drugiego, wiem, że funkcja będzie nadal działać z zachowaniem całej dotychczasowej tradycji testowania. Jeśli mam import na górze modułu, kiedy przenoszę funkcję, okazuje się, że spędzam dużo czasu na kompletnym i minimalnym imporcie nowego modułu. Refaktoryzacja IDE może uczynić to nieistotnym.
Istnieje kara prędkości, jak wspomniano w innym miejscu. Zmierzyłem to w swojej aplikacji i uznałem, że jest to nieistotne dla moich celów.
Miło jest także widzieć z góry wszystkie zależności modułów bez konieczności wyszukiwania (np. Grep). Jednak zależy mi na zależnościach modułów, ponieważ instaluję, refaktoryzuję lub przenoszę cały system zawierający wiele plików, a nie tylko jeden moduł. W takim razie i tak przeprowadzę globalne wyszukiwanie, aby upewnić się, że mam zależności na poziomie systemu. Nie znalazłem więc globalnego importu, który pomógłby mi zrozumieć system w praktyce.
Zwykle umieszczam import w
sys
środkuif __name__=='__main__'
czeku, a następnie przekazuję argumenty (jaksys.argv[1:]
) domain()
funkcji. To pozwala mi używaćmain
w kontekście, w którymsys
nie został zaimportowany.źródło
def main(): print(sys.argv); if True: import sys; main();
Trzeba by zawinąćif __name__=='__main__'
funkcję, aby utworzyć nową przestrzeń nazw.Przez większość czasu byłoby to użyteczne ze względu na przejrzystość i sensowne, ale nie zawsze tak jest. Poniżej znajduje się kilka przykładów okoliczności, w których import modułów może znajdować się gdzie indziej.
Po pierwsze, możesz mieć moduł z testem jednostkowym formularza:
Po drugie, może być wymagany warunkowy import innego modułu w czasie wykonywania.
Prawdopodobnie istnieją inne sytuacje, w których możesz umieścić import w innych częściach kodu.
źródło
Pierwszy wariant jest rzeczywiście bardziej wydajny niż drugi, gdy funkcja jest wywoływana albo zero, albo jeden raz. Jednak przy drugim i kolejnych wywołaniach podejście „importuj każde połączenie” jest w rzeczywistości mniej wydajne. Zobacz ten link, aby zapoznać się z techniką leniwego ładowania, która łączy najlepsze z obu podejść, wykonując „leniwy import”.
Ale są powody inne niż wydajność, dlatego możesz preferować jedno od drugiego. Jednym podejściem jest to, że osoba czytająca kod o wiele lepiej wyjaśnia zależności, które ma ten moduł. Mają także bardzo różne charakterystyki awarii - pierwsza zawiedzie w czasie ładowania, jeśli nie ma modułu „datetime”, a druga nie zawiedzie, dopóki nie zostanie wywołana metoda.
Dodano Uwaga: W IronPython importowanie może być nieco droższe niż w CPython, ponieważ kod jest w zasadzie kompilowany podczas importowania.
źródło
Curt ma rację: druga wersja jest bardziej przejrzysta i zawiedzie w czasie ładowania, a nie później i nieoczekiwanie.
Zwykle nie martwię się o wydajność ładowania modułów, ponieważ jest to (a) dość szybkie i (b) najczęściej dzieje się to tylko przy starcie.
Jeśli musisz ładować moduły ciężkie w nieoczekiwanych momentach, prawdopodobnie bardziej sensowne jest ładowanie ich dynamicznie za pomocą
__import__
funkcji i pamiętaj o przechwytywaniuImportError
wyjątków i obsługiwaniu ich w rozsądny sposób.źródło
Nie martwiłbym się zbytnio wydajnością ładowania modułu z przodu. Pamięć zajmowana przez moduł nie będzie bardzo duża (zakładając, że jest wystarczająco modułowa), a koszt uruchomienia będzie znikomy.
W większości przypadków chcesz załadować moduły na górze pliku źródłowego. Ktoś czytający Twój kod znacznie ułatwia stwierdzenie, która funkcja lub obiekt pochodzi z jakiego modułu.
Jednym dobrym powodem do zaimportowania modułu w innym miejscu kodu jest użycie go w instrukcji debugowania.
Na przykład:
Mogę to debugować za pomocą:
Oczywiście innym powodem importowania modułów w innym miejscu w kodzie jest konieczność dynamicznego importowania ich. Jest tak, ponieważ właściwie nie masz wyboru.
Nie martwiłbym się zbytnio wydajnością ładowania modułu z przodu. Pamięć zajmowana przez moduł nie będzie bardzo duża (zakładając, że jest wystarczająco modułowa), a koszt uruchomienia będzie znikomy.
źródło
Jest to kompromis, który może podjąć tylko programista.
Przypadek 1 pozwala zaoszczędzić trochę pamięci i czasu uruchamiania, nie importując modułu datetime (i wykonując dowolną inicjalizację, której może potrzebować), dopóki nie będzie potrzebny. Należy pamiętać, że importowanie „tylko po wywołaniu” oznacza również wykonanie go „za każdym razem, gdy jest wywoływany”, więc każde połączenie po pierwszym nadal wiąże się z dodatkowym kosztem wykonania importu.
Przypadek 2 pozwala zaoszczędzić trochę czasu i opóźnień wykonania, importując wcześniej datetime, aby funkcja not_often_called () zwróciła się szybciej, gdy jest nazywa, a także nie ponosząc napowietrznej importu na każde wezwanie.
Oprócz wydajności łatwiej jest z góry zobaczyć zależności modułów, jeśli instrukcje importu są ... z góry. Ukrywanie ich w kodzie może utrudnić znalezienie modułów, od których coś zależy.
Osobiście zazwyczaj śledzę PEP, z wyjątkiem rzeczy takich jak testy jednostkowe i takie, że nie chcę zawsze ładować, ponieważ wiem, że nie będą używane z wyjątkiem kodu testowego.
źródło
sys.modules
można łatwo zrównoważyć oszczędnościami wynikającymi z konieczności wyszukiwania nazwy lokalnej zamiast nazwy globalnej.Oto przykład, w którym wszystkie importowane są na samym szczycie (to jedyny raz, kiedy musiałem to zrobić). Chcę mieć możliwość zakończenia podprocesu zarówno w systemie Un * x, jak i Windows.
(W recenzji: co powiedział John Millikin .)
źródło
To jest jak wiele innych optymalizacji - poświęcasz trochę czytelności dla prędkości. Jak wspomniał John, jeśli odrobiłeś pracę domową z profilowania i zauważyłeś, że jest to wystarczająco przydatna zmiana i potrzebujesz dodatkowej prędkości, to idź. Prawdopodobnie dobrze byłoby umieścić notatkę na temat wszystkich innych importów:
źródło
Inicjalizacja modułu następuje tylko raz - przy pierwszym imporcie. Jeśli dany moduł pochodzi ze standardowej biblioteki, prawdopodobnie zaimportujesz go również z innych modułów w programie. W przypadku modułu tak powszechnego jak data i godzina jest to prawdopodobnie zależność od wielu innych standardowych bibliotek. Instrukcja importu kosztowałaby wtedy bardzo niewiele, ponieważ już zainicjalizowano by moduł. Wszystko, co robi w tym momencie, to powiązanie istniejącego obiektu modułu z zakresem lokalnym.
Połącz tę informację z argumentem za czytelnością i powiedziałbym, że najlepiej jest mieć instrukcję importu w zakresie modułu.
źródło
Aby wypełnić odpowiedź Moe i pierwotne pytanie:
Kiedy mamy do czynienia z zależnościami cyklicznymi, możemy wykonać kilka „sztuczek”. Zakładając, że pracujemy z modułami
a.py
ib.py
które zawierająx()
i by()
, odpowiednio. Następnie:from imports
dolnej części modułu.from imports
funkcji lub metody, która faktycznie wymaga importu (nie zawsze jest to możliwe, ponieważ możesz jej użyć z kilku miejsc).from imports
aby wyglądał jak import:import a
Podsumowując. Jeśli nie masz do czynienia z zależnościami cyklicznymi i robisz jakąś sztuczkę, aby ich uniknąć, lepiej umieścić wszystkie swoje importy na górze z powodów wyjaśnionych już w innych odpowiedziach na to pytanie. I proszę, robiąc te „sztuczki” z komentarzem, zawsze jest to mile widziane! :)
źródło
Oprócz podanych już doskonałych odpowiedzi, warto zauważyć, że umieszczenie importu nie jest tylko kwestią stylu. Czasami moduł ma niejawne zależności, które należy najpierw zaimportować lub zainicjować, a import najwyższego poziomu może prowadzić do naruszenia wymaganej kolejności wykonywania.
Ten problem często pojawia się w interfejsie API języka Python Apache Spark, w którym należy zainicjować SparkContext przed zaimportowaniem pakietów lub modułów pyspark. Najlepiej jest umieścić import pyspark w zakresie, w którym SparkContext ma zagwarantowaną dostępność.
źródło
Zaskoczyło mnie, że nie opublikowałem już faktycznych liczb kosztów powtórnych kontroli obciążenia, chociaż istnieje wiele dobrych wyjaśnień, czego można się spodziewać.
Jeśli importujesz u góry, to niezależnie od tego, jaki ładunek trafisz. To dość małe, ale zwykle w milisekundach, a nie nanosekundach.
Jeśli importujesz w ramach funkcji, wtedy trafiasz tylko do załadowania, jeśli i kiedy jedna z tych funkcji jest wywoływana po raz pierwszy. Jak wielu zauważyło, jeśli tak się nie stanie, oszczędzasz czas ładowania. Ale jeśli funkcja (y) nazywa się dużo, można podjąć powtarzające się mimo znacznie mniejszej trafienia (dla sprawdzenia, że został załadowany; nie za faktycznie przeładunkowych). Z drugiej strony, jak zauważył @aaronasterling, oszczędzasz również trochę, ponieważ importowanie w funkcji pozwala funkcji na nieco szybsze wyszukiwanie zmiennych lokalnych w celu późniejszego zidentyfikowania nazwy ( http://stackoverflow.com/questions/477096/python- import-koding-style / 4789963 # 4789963 ).
Oto wyniki prostego testu, który importuje kilka rzeczy z wnętrza funkcji. Podane czasy (w Pythonie 2.7.14 na procesorze Intel Core i7 2,3 GHz) pokazano poniżej (drugie połączenie odbierające więcej niż później wydaje się spójne, choć nie wiem dlaczego).
Kod:
źródło
Nie dążę do udzielenia pełnej odpowiedzi, ponieważ inni już to zrobili bardzo dobrze. Chciałbym tylko wspomnieć o jednym przypadku użycia, gdy uważam, że szczególnie przydatne jest importowanie modułów do funkcji. Moja aplikacja używa pakietów i modułów Pythona przechowywanych w określonej lokalizacji jako wtyczek. Podczas uruchamiania aplikacji aplikacja przechodzi przez wszystkie moduły w lokalizacji i importuje je, a następnie zagląda do modułów i jeśli znajdzie jakieś punkty montażowe dla wtyczek (w moim przypadku jest to podklasa pewnej klasy podstawowej o unikatowej klasie ID) rejestruje je. Liczba wtyczek jest duża (obecnie kilkadziesiąt, ale może setki w przyszłości) i każda z nich jest używana dość rzadko. Importowanie bibliotek stron trzecich na górze moich modułów wtyczek było nieco karalne podczas uruchamiania aplikacji. Szczególnie niektóre biblioteki stron trzecich są ciężkie do zaimportowania (np. Import spisku próbuje nawet połączyć się z Internetem i pobrać coś, co dodaje około jednej sekundy do uruchomienia). Dzięki optymalizacji importu (wywoływanie ich tylko w funkcjach, w których są używane) we wtyczkach udało mi się skrócić start z 10 sekund do około 2 sekund. To duża różnica dla moich użytkowników.
Więc moja odpowiedź brzmi: nie, nie zawsze umieszczaj import na szczycie swoich modułów.
źródło
Interesujące jest to, że jak dotąd żadna odpowiedź nie wspomniała o przetwarzaniu równoległym, gdzie WYMAGANE może być, że import jest w funkcji, gdy serializowany kod funkcji jest przekazywany do innych rdzeni, np. Jak w przypadku ipyparallel.
źródło
Zwiększenie wydajności może nastąpić poprzez zaimportowanie zmiennych / lokalnego zakresu do funkcji. Zależy to od użycia importowanej rzeczy w funkcji. Jeśli zapętlasz się wiele razy i uzyskujesz dostęp do globalnego obiektu modułu, importowanie go jako lokalnego może pomóc.
test.py
runlocal.py
run.py
Czas w Linuksie pokazuje niewielki zysk
prawdziwy jest zegar ścienny. użytkownik ma czas w programie. sys to czas na wywołania systemowe.
https://docs.python.org/3.5/reference/executionmodel.html#resolution-of-names
źródło
Czytelność
Oprócz wydajności uruchamiania, do zlokalizowania
import
instrukcji należy podać argument dotyczący czytelności . Na przykład weźmy numery linii python od 1283 do 1296 w moim bieżącym pierwszym projekcie Python:Jeśli
import
stwierdzenie znajdowało się na początku pliku, musiałbym przewinąć w górę długą drogę lub nacisnąć Home, aby dowiedzieć się, coET
było. Następnie musiałbym wrócić do wiersza 1283, aby kontynuować czytanie kodu.Rzeczywiście, nawet gdyby
import
instrukcja znajdowała się na górze funkcji (lub klasy), jak wiele by ją umieściło, konieczne byłoby stronicowanie w górę i w dół.Wyświetlanie numeru wersji Gnome rzadko jest wykonywane, więc
import
u góry pliku wprowadzono niepotrzebne opóźnienie uruchamiania.źródło
Chciałbym wspomnieć o moim przypadku użycia, bardzo podobnym do tych wymienionych przez @Johna Millikina i @VK:
Opcjonalne importy
Analizuję dane za pomocą Notatnika Jupyter i używam tego samego notesu IPython jako szablonu do wszystkich analiz. W niektórych przypadkach muszę zaimportować Tensorflow, aby wykonać szybkie przebiegi modeli, ale czasami pracuję w miejscach, w których tensorflow nie jest skonfigurowany / wolno go importuje. W takich przypadkach hermetyzuję swoje operacje zależne od Tensorflow w funkcji pomocniczej, importuję tensorflow do tej funkcji i wiążę ją z przyciskiem.
W ten sposób mogłem zrobić „restart i uruchom wszystko” bez konieczności oczekiwania na import lub wznawiania reszty komórek, gdy się nie powiedzie.
źródło
To fascynująca dyskusja. Jak wiele innych, nigdy nawet nie rozważałem tego tematu. Zaskoczyło mnie, że muszę importować funkcje, ponieważ chcę używać Django ORM w jednej z moich bibliotek. Musiałem zadzwonić
django.setup()
przed zaimportowaniem moich klas modeli, a ponieważ znajdował się on na górze pliku, był przeciągany do kodu biblioteki innego niż Django z powodu konstrukcji wtryskiwacza IoC.W pewnym sensie trochę zhackowałem i ostatecznie
django.setup()
umieściłem konstruktora singletona i odpowiedni import na górze każdej metody klasy. Teraz to działało dobrze, ale sprawiło, że poczułem się nieswojo, ponieważ import nie był na szczycie, a także zacząłem martwić się dodatkowym czasem importu. Potem przyszedłem i czytałem z wielkim zainteresowaniem, że wszyscy się tym zajmują.Mam długie doświadczenie w C ++ i teraz używam Python / Cython. Podejrzewam, że dlaczego nie umieścić importu w funkcji, chyba że spowoduje to profilowane wąskie gardło. To tylko jak deklarowanie miejsca dla zmiennych tuż przed ich użyciem. Problem w tym, że mam tysiące linii kodu ze wszystkimi importami na górze! Więc myślę, że zrobię to odtąd i zmieniam nieparzysty plik tu i tam, kiedy przechodzę i mam czas.
źródło