Czy instrukcje importu powinny zawsze znajdować się na górze modułu?

403

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()
Adam J. Forster
źródło

Odpowiedzi:

283

Import modułu jest dość szybki, ale nie natychmiastowy. To znaczy że:

  • Umieszczenie importu na górze modułu jest w porządku, ponieważ jest to trywialny koszt, który jest płacony tylko raz.
  • Umieszczenie importu w funkcji spowoduje, że wywołanie tej funkcji będzie trwało dłużej.

Jeśli więc zależy Ci na wydajności, umieść import na górze. Przenieś je do funkcji tylko wtedy, gdy profilowanie pokazuje, że to pomogłoby ( zrobiłeś profil, aby zobaczyć, gdzie najlepiej poprawić wydajność, prawda?)


Najlepsze powody, dla których widziałem leniwe importowanie, to:

  • Opcjonalna obsługa bibliotek. Jeśli twój kod ma wiele ścieżek korzystających z różnych bibliotek, nie psuj się, jeśli opcjonalna biblioteka nie jest zainstalowana.
  • W __init__.pywtyczce, która może zostać zaimportowana, ale nie jest faktycznie używana. Przykładami są wtyczki Bazaar, które wykorzystują bzrlibleniwe środowisko ładujące.
John Millikin
źródło
17
John, to było całkowicie teoretyczne pytanie, więc nie miałem żadnego kodu do profilowania. W przeszłości zawsze śledziłem PEP, ale dzisiaj napisałem jakiś kod, który sprawił, że zastanawiałem się, czy to było właściwe. Dzięki za pomoc.
Adam J. Forster,
43
> Umieszczenie importu w funkcji spowoduje, że wywołanie tej funkcji będzie trwało dłużej. Właściwie uważam, że ten koszt jest płacony tylko raz. Czytałem, że Python buforuje importowany moduł, więc ponowny import jest minimalny.
stopiony kształt
24
@halfhourhacks Python nie zaimportuje ponownie modułu, ale musi wykonać kilka instrukcji, aby sprawdzić, czy moduł istnieje / znajduje się w sys.modules / itp.
John Millikin
24
-1. Umieszczenie importu w funkcji niekoniecznie powoduje, że trwa dłużej. Proszę zobaczyć moją odpowiedź na inne pytanie.
aaronasterling
4
Jednym z przypadków użycia jest unikanie importu cyklicznego (zwykle nie jest to rozsądne, ale czasami jest odpowiednie). Czasami klasa A w module m1 wywołuje metodę w klasie B w module m2, która konstruuje inną instancję klasy A. Jeśli metoda w klasie B, która konstruuje instancję klasy A, importuje się tylko po uruchomieniu funkcji konstruującej instancję, unika się importu cyklicznego.
Sam Svenbjorgchristiensensen
80

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

Moe
źródło
3
Tak, ale można dostać się do piekła uzależnienia.
eigenein
8
Jeśli dwa moduły muszą się nawzajem importować, coś jest nie tak z kodem.
Anna,
Programowanie obiektowe często prowadzi mnie do zależności cyklicznych. Istotną klasę obiektów można zaimportować do kilku modułów. Aby ten obiekt mógł wykonywać swoje własne zadania, może być konieczne sięgnięcie do jednego lub więcej z tych modułów. Istnieją sposoby, aby tego uniknąć, na przykład wysyłanie danych do obiektu za pomocą argumentów funkcji, aby umożliwić mu dostęp do innego modułu. Są jednak chwile, w których robienie tego jest dla OOP bardzo sprzeczne z intuicją (świat zewnętrzny nie powinien wiedzieć, jak wykonuje to zadanie w tej funkcji).
Robert,
4
Kiedy X potrzebuje Y, a Y potrzebuje X, są to albo dwie części tego samego pomysłu (tj. Powinny być zdefiniowane razem), albo brakuje abstrakcji.
GLRoman
59

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środku if __name__=='__main__'czeku, a następnie przekazuję argumenty (jak sys.argv[1:]) do main()funkcji. To pozwala mi używać mainw kontekście, w którym sysnie został zaimportowany.


źródło
4
Wiele IDE ułatwia to refaktoryzowanie kodu przez optymalizację i automatyczne importowanie niezbędnych modułów do pliku. W większości przypadków PyCharm i Eclipse podjęły za mnie właściwe decyzje. Założę się, że istnieje sposób na uzyskanie takiego samego zachowania w emacs lub vim.
brent.payne
3
Import wewnątrz instrukcji if w globalnej przestrzeni nazw jest nadal importem globalnym. Spowoduje to wydrukowanie argumentów (za pomocą Pythona 3): def main(): print(sys.argv); if True: import sys; main();Trzeba by zawinąć if __name__=='__main__'funkcję, aby utworzyć nową przestrzeń nazw.
Darcinon
4
Uderza mnie to jako doskonały powód do importowania w ramach funkcji, a nie w zakresie globalnym. Jestem zaskoczony, że nikt inny nie wspominał o robieniu tego z tego samego powodu. Czy są jakieś istotne wady, oprócz wydajności i gadatliwości?
algal
@algal minusem jest to, że wiele osób python nienawidzi tego, ponieważ naruszasz kodeks pep. Musisz przekonać członków swojego zespołu. Kara za wydajność jest minimalna. Czasami jest jeszcze szybszy, patrz stackoverflow.com/a/4789963/362951
mit
Przekonałem się, że niezwykle przydatne przy refaktoryzacji jest umieszczenie importu blisko miejsca, w którym go używam. Nie trzeba już przewijać do góry i do tyłu tylu timów. Używam IDE, takich jak pycharm lub wing ide, a także używam ich refaktoryzacji, ale nie zawsze chcę na nich polegać. Przenoszenie funkcji do innego modułu staje się o wiele łatwiejsze dzięki temu alternatywnemu stylowi importowania, w związku z czym znacznie bardziej refaktoryzuję.
mit
39

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:

if __name__ == '__main__':
    import foo
    aa = foo.xyz()         # initiate something for the test

Po drugie, może być wymagany warunkowy import innego modułu w czasie wykonywania.

if [condition]:
    import foo as plugin_api
else:
    import bar as plugin_api
xx = plugin_api.Plugin()
[...]

Prawdopodobnie istnieją inne sytuacje, w których możesz umieścić import w innych częściach kodu.

ConcernedOfTunbridgeWells
źródło
14

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.

Curt Hagenlocher
źródło
1
To nieprawda, że ​​pierwszy działa lepiej: wiki.python.org/moin/PythonSpeed/…
Jason Baker
Działa lepiej, jeśli metoda nigdy nie jest wywoływana, ponieważ import nigdy się nie dzieje.
Curt Hagenlocher 24.09.08
To prawda, ale działa gorzej, jeśli metoda jest wywoływana więcej niż jeden raz. A korzyści w zakresie wydajności, które zyskałbyś z niezwłocznego importowania modułu, są w większości przypadków nieistotne. Wyjątkiem byłyby sytuacje, gdy moduł jest bardzo duży lub istnieje wiele tego rodzaju funkcji.
Jason Baker
W świecie IronPython początkowy import jest znacznie droższy niż w CPython;). Przykład „leniwego importu” w twoim linku jest prawdopodobnie najlepszym ogólnym rozwiązaniem.
Curt Hagenlocher 24.09.08
Mam nadzieję, że nie masz nic przeciwko, ale edytowałem to w swoim poście. To przydatne informacje, które należy wiedzieć.
Jason Baker
9

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 przechwytywaniu ImportErrorwyjątków i obsługiwaniu ich w rozsądny sposób.

Dan Lenski
źródło
8

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:

do_something_with_x(x)

Mogę to debugować za pomocą:

from pprint import pprint
pprint(x)
do_something_with_x(x)

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.

Jason Baker
źródło
Mówimy o dziesiątkach milisekund kosztu uruchomienia na moduł (na mojej maszynie). Nie zawsze jest to nieistotne, np. Jeśli wpływa na reakcję aplikacji internetowej na kliknięcie użytkownika.
Evgeni Sergeev
6

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.

pjz
źródło
2
-1. Główny koszt importowania występuje tylko za pierwszym razem. Koszt wyszukiwania modułu sys.modulesmożna łatwo zrównoważyć oszczędnościami wynikającymi z konieczności wyszukiwania nazwy lokalnej zamiast nazwy globalnej.
aaronasterling 25.01.11
6

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.

import os
# ...
try:
    kill = os.kill  # will raise AttributeError on Windows
    from signal import SIGTERM
    def terminate(process):
        kill(process.pid, SIGTERM)
except (AttributeError, ImportError):
    try:
        from win32api import TerminateProcess  # use win32api if available
        def terminate(process):
            TerminateProcess(int(process._handle), -1)
    except ImportError:
        def terminate(process):
            raise NotImplementedError  # define a dummy function

(W recenzji: co powiedział John Millikin .)

giltay
źródło
6

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:

from foo import bar
from baz import qux
# Note: datetime is imported in SomeClass below
Drew Stephens
źródło
4

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.

Jeremy Brown
źródło
4

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.pyi b.pyktóre zawierają x()i b y(), odpowiednio. Następnie:

  1. Możemy przenieść jeden z from importsdolnej części modułu.
  2. Możemy przenieść jedną z from importsfunkcji lub metody, która faktycznie wymaga importu (nie zawsze jest to możliwe, ponieważ możesz jej użyć z kilku miejsc).
  3. Możemy zmienić jeden z dwóch, from importsaby 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! :)

Kumony
źródło
4

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ść.

Paweł
źródło
4

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).

 0 foo:   14429.0924 µs
 1 foo:      63.8962 µs
 2 foo:      10.0136 µs
 3 foo:       7.1526 µs
 4 foo:       7.8678 µs
 0 bar:       9.0599 µs
 1 bar:       6.9141 µs
 2 bar:       7.1526 µs
 3 bar:       7.8678 µs
 4 bar:       7.1526 µs

Kod:

from __future__ import print_function
from time import time

def foo():
    import collections
    import re
    import string
    import math
    import subprocess
    return

def bar():
    import collections
    import re
    import string
    import math
    import subprocess
    return

t0 = time()
for i in xrange(5):
    foo()
    t1 = time()
    print("    %2d foo: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6))
    t0 = t1
for i in xrange(5):
    bar()
    t1 = time()
    print("    %2d bar: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6))
    t0 = t1
TextGeek
źródło
Zmiany w czasie wykonywania są prawdopodobnie spowodowane skalowaniem częstotliwości procesora w odpowiedzi na obciążenie. Lepiej jest rozpocząć testy prędkości od sekundy zajętej pracy, aby zwiększyć szybkość zegara procesora.
Han-Kwang Nienhuys
3

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.

VK
źródło
3

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.

K.-Michael Aye
źródło
1

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

X=10
Y=11
Z=12
def add(i):
  i = i + 10

runlocal.py

from test import add, X, Y, Z

    def callme():
      x=X
      y=Y
      z=Z
      ladd=add 
      for i  in range(100000000):
        ladd(i)
        x+y+z

    callme()

run.py

from test import add, X, Y, Z

def callme():
  for i in range(100000000):
    add(i)
    X+Y+Z

callme()

Czas w Linuksie pokazuje niewielki zysk

/usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python run.py 
    0:17.80 real,   17.77 user, 0.01 sys
/tmp/test$ /usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python runlocal.py 
    0:14.23 real,   14.22 user, 0.01 sys

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

cichy pingwin
źródło
1

Czytelność

Oprócz wydajności uruchamiania, do zlokalizowania importinstrukcji 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:

listdata.append(['tk font version', font_version])
listdata.append(['Gtk version', str(Gtk.get_major_version())+"."+
                 str(Gtk.get_minor_version())+"."+
                 str(Gtk.get_micro_version())])

import xml.etree.ElementTree as ET

xmltree = ET.parse('/usr/share/gnome/gnome-version.xml')
xmlroot = xmltree.getroot()
result = []
for child in xmlroot:
    result.append(child.text)
listdata.append(['Gnome version', result[0]+"."+result[1]+"."+
                 result[2]+" "+result[3]])

Jeśli importstwierdzenie znajdowało się na początku pliku, musiałbym przewinąć w górę długą drogę lub nacisnąć Home, aby dowiedzieć się, co ETbyło. Następnie musiałbym wrócić do wiersza 1283, aby kontynuować czytanie kodu.

Rzeczywiście, nawet gdyby importinstrukcja 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 importu góry pliku wprowadzono niepotrzebne opóźnienie uruchamiania.

WinEunuuchs2Unix
źródło
0

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.

Cedr
źródło
0

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.

LJHW
źródło