Dlaczego „import *” jest zły?


153

Zaleca się, aby nie używać import *w Pythonie.

Czy ktoś może podać powód takiego stanu rzeczy, abym mógł tego uniknąć następnym razem?



2
zależy to od tego, czy piszesz skrypt lub kod, którego musisz użyć ponownie. czasami opłaca się ignorować standardy kodu. „import *” może być również w porządku, jeśli masz konwencję nazewnictwa, która jasno określa, skąd się wzięły. np. „from Cats import *; TabbyCat; MaineCoonCat; CalicoCat;”
gatoatigrado

3
import *u mnie nie działa na pierwszym miejscu w Pythonie 2 lub 3.
joshreesjones

1
Czy to odpowiada na twoje pytanie? Co dokładnie oznacza import „import *”?
AMC

Odpowiedzi:


223
  • Ponieważ umieszcza dużo rzeczy w twojej przestrzeni nazw (może przesłonić jakiś inny obiekt z poprzedniego importu i nie będziesz o tym wiedział).

  • Ponieważ nie wiesz dokładnie, co jest importowane i nie możesz łatwo znaleźć, z którego modułu dana rzecz została zaimportowana (czytelność).

  • Ponieważ nie możesz używać fajnych narzędzi, takich jak pyflakesstatyczne wykrywanie błędów w kodzie.


2
Tak, naprawdę nienawidzę swojej pracy, gdy ktoś używa * import, ponieważ nie mogę po prostu uruchomić pyflakes i być szczęśliwym, ale muszę naprawić te importy. Fajnie jednak, że z tymi pyflakes mi pomaga :-)
gruszczy

7
Jako konkretny przykład, wielu użytkowników NumPy zostało ugryzionych przez numpy.anycieniowanie, anykiedy to robią, from numpy import *lub „pomocne” narzędzie robi to za nich.
user2357112 obsługuje Monikę

1
Czy powinienem unikać używania przełącznika --pylab dla IPython z tych samych powodów?
timgeb

6
Aby wyróżnić się z ryzykiem, że nigdy nie myślał o przed przeczytaniem tego ( „cień może jakiś inny obiekt z poprzedniego importu”) import *sprawia, że kolejność tych importwypowiedzi znaczących ... nawet dla standardowych modułów bibliotecznych, które zazwyczaj nie dbają o celu importu . Coś tak niewinnego, jak alfabetowanie importwypowiedzi, może złamać twój skrypt, gdy była ofiara wojny importowej zostanie jedyną ocalałą. (Nawet jeśli twój skrypt działa teraz i nigdy się nie zmienia, może nagle zawieść jakiś czas później, jeśli zaimportowany moduł wprowadzi nową nazwę, która zastąpi tę, na której polegałeś.)
Kevin J. Chase,

49

Według Zen of Python :

Jawne jest lepsze niż niejawne.

... na pewno nie możesz się z tym kłócić?


29
Właściwie, to można się z tym kłócić. Jest to również całkowicie niespójne, biorąc pod uwagę, że nie deklarujesz zmiennych jawnie w Pythonie, po prostu pojawiają się one po przypisaniu do nich.
Konrad Rudolph

7
@gruszczy: deklarowanie zmiennych jest zbędne do czego ? Przypisujesz? Nie, to dwie odrębne koncepcje, a deklaracja czegoś przekazuje bardzo wyraźną i ważną informację. W każdym razie jednoznaczność jest zawsze w jakiś sposób związana z redundancją, to dwie strony tej samej monety.
Konrad Rudolph

3
@kriss prawda, ale nie o to mi chodziło. Chodziło mi o to, że brak jawnego zadeklarowania zmiennej prowadzi do błędów. Mówisz, że „zlecenie bez [deklaracji] jest niemożliwe”. Ale to źle, chodzi mi o to, że niestety Python dokładnie to umożliwia.
Konrad Rudolph

3
@kriss Kolejną informacją przekazywaną kompilatorowi przez deklarację jest fakt, że rzeczywiście zamierzasz zadeklarować nową zmienną. To kluczowa informacja dla systemu czcionek. Mówisz, że współczesne IDE rozwiązują problem błędnego wpisywania, ale jest to po prostu błędne. W rzeczywistości jest to poważny problem w językach niekompilowanych statycznie, dlatego Perl dodał use strict(JavaScript var). Nawiasem mówiąc , oczywiście Python nie jest bez typu (w rzeczywistości jest silnie wpisany). W każdym razie, nawet gdybyś miał rację, nadal byłoby to sprzeczne z Zen Pythona, cytowanym w tej odpowiedzi.
Konrad Rudolph

3
@kriss Źle: ponowne użycie tej samej nazwy zmiennej nie jest problemem - ponowne użycie tej samej zmiennej jest (tj. ta sama nazwa w tym samym zakresie). Jawne deklaracje zapobiegłyby dokładnie temu błędowi (i innym, opartym na prostym błędnym wpisywaniu, co, jak powiedziałem, jest w rzeczywistości niezwykle powszechnym i czasochłonnym problemem, mimo że masz rację, że problem jest większy w Perlu Języki). A sprzecznością, do której nawiązuję, jest wymóg zen dotyczący jawności, który jest tutaj fizycznie wyrzucany przez okno.
Konrad Rudolph

40

Nie przechodzisz **locals()do funkcji, prawda?

Ponieważ Python brakuje „zawierać” oświadczenie, aself parametr jest wyraźny, a zasady określania zakresu są dość proste, to zwykle bardzo łatwo wskazać palcem na zmiennej i powiedzieć, gdzie ten przedmiot pochodzi od - bez czytania innych modułów i bez jakiejkolwiek IDE (które i tak są ograniczone ze względu na introspekcję, ponieważ język jest bardzo dynamiczny).

To import *wszystko psuje.

Posiada również konkretną możliwość ukrywania błędów.

import os, sys, foo, sqlalchemy, mystuff
from bar import *

Teraz, jeśli moduł bar ma którykolwiek z atrybutów „ os”, „ mystuff”, itp., Nadpisuje atrybuty jawnie zaimportowane i prawdopodobnie wskaże na bardzo różne rzeczy. Definiowanie __all__w pasku jest często rozsądne - określa to, co zostanie niejawnie zaimportowane - ale nadal trudno jest prześledzić, skąd pochodzą obiekty, bez czytania i analizowania modułu paska i śledzenia jego importu. Sieć import *to pierwsza rzecz, którą naprawiam, gdy przejmuję projekt na własność.

Nie zrozum mnie źle: gdyby tego import *brakowało, płakałbym, żeby go mieć. Ale należy go używać ostrożnie. Dobrym przypadkiem użycia jest zapewnienie interfejsu fasady nad innym modułem. Podobnie, użycie warunkowych instrukcji importu lub importu wewnątrz przestrzeni nazw funkcji / klas wymaga pewnej dyscypliny.

Myślę, że w średnich i dużych projektach lub małych z kilkoma współpracownikami, minimalna higiena jest potrzebna pod względem analizy statycznej - uruchomienie przynajmniej pyflakes lub jeszcze lepiej odpowiednio skonfigurowanego pylinta - aby złapać kilka rodzajów błędów wcześniej zdarzają się.

Oczywiście, ponieważ jest to Python - nie krępuj się łamać zasad i eksplorować - ale uważaj na projekty, które mogą wzrosnąć dziesięciokrotnie, jeśli w kodzie źródłowym brakuje dyscypliny, będzie to problem.


6
Python 2.x nie mają „include” oświadczenia. To się nazywa execfile(). Na szczęście jest rzadko używany i zniknął w 3.x.
Sven Marnach

Co powiesz **vars()na dołączenie zmiennych globalnych, jeśli wywoływana funkcja znajduje się w innym pliku? : P
Solomon Ucko

16

Można to zrobić from ... import *w sesji interaktywnej.


A może wewnątrz doctestsznurka? Czy import *w tym przypadku jest interpretowany wewnątrz „piaskownicy”? Dzięki.
PatrickT

16

Dzieje się tak, ponieważ zanieczyszczasz przestrzeń nazw. Zaimportujesz wszystkie funkcje i klasy do własnej przestrzeni nazw, co może kolidować z funkcjami, które sam zdefiniujesz.

Ponadto uważam, że użycie nazwy kwalifikowanej jest bardziej zrozumiałe w przypadku zadania konserwacyjnego; w samym wierszu kodu widzisz, skąd pochodzi funkcja, dzięki czemu możesz znacznie łatwiej sprawdzić dokumenty.

W module foo:

def myFunc():
    print 1

W swoim kodzie:

from foo import *

def doThis():
    myFunc() # Which myFunc is called?

def myFunc():
    print 2


9

Powiedzmy, że masz następujący kod w module o nazwie foo:

import ElementTree as etree

a następnie we własnym module masz:

from lxml import etree
from foo import *

Masz teraz trudny do debugowania moduł, który wygląda tak , jakby zawierał w sobie etree lxml, ale tak naprawdę zawiera ElementTree.


7

To wszystko są dobre odpowiedzi. Dodam, że ucząc nowych ludzi programowania w Pythonie, radzenie sobie import *jest bardzo trudne. Nawet jeśli Ty lub oni nie napisaliście kodu, nadal jest to przeszkoda.

Uczę dzieci (około 8 lat) programowania w Pythonie do manipulowania Minecrafta. Lubię udostępniać im pomocne środowisko programistyczne do pracy z ( Edytor Atom ) i uczyć programowania opartego na REPL (przez bpython ). W Atom stwierdzam, że podpowiedzi / uzupełnienia działają równie skutecznie jak bpython. Na szczęście, w przeciwieństwie do innych narzędzi do analizy statystycznej, Atom nie daje się zwieść import *.

Jednak weźmy ten przykład ... W tym opakowaniufrom local_module import *to zestawy modułów, w tym ta lista bloków . Zignorujmy ryzyko kolizji przestrzeni nazw. Robiąc from mcpi.block import *to, sprawiają, że cała lista niejasnych typów bloków jest czymś, na co musisz spojrzeć, aby wiedzieć, co jest dostępne. Gdyby zamiast tego użyli from mcpi import block, możesz wpisać, walls = block.a następnie pojawi się lista autouzupełniania. Zrzut ekranu Atom.io


6

Zrozumiałem ważne punkty, które ludzie tutaj umieścili. Mam jednak jeden argument, że czasami „importowanie z gwiazd” nie zawsze może być złą praktyką:

  • Kiedy chcę zorganizować swój kod w taki sposób, aby wszystkie stałe trafiały do ​​modułu o nazwie const.py :
    • Jeśli tak import const, to dla każdej stałej muszę nazywać to const.SOMETHING, co prawdopodobnie nie jest najwygodniejszym sposobem.
    • Jeśli zrobię from const import SOMETHING_A, SOMETHING_B ... , to oczywiście jest to zbyt rozwlekłe i mija się z celem strukturyzacji.
    • Dlatego uważam, że w tym przypadku zrobienie a from const import *może być lepszym wyborem.

4

Jest to bardzo ZŁA praktyka z dwóch powodów:

  1. Czytelność kodu
  2. Ryzyko nadpisania zmiennych / funkcji itp

Do punktu 1 : Zobaczmy przykład w ten sposób:

from module1 import *
from module2 import *
from module3 import *

a = b + c - d

Tutaj, widząc kod nie będzie dostać pojęcia dotyczące, z którego moduł b, cid właściwie należy.

Z drugiej strony, jeśli robisz to jak:

#                   v  v  will know that these are from module1
from module1 import b, c   # way 1
import module2             # way 2

a = b + c - module2.d
#            ^ will know it is from module2

Jest to dla Ciebie dużo czystsze, a także nowa osoba dołączająca do Twojego zespołu będzie miała lepszy pomysł.

Dla punktu 2 : Powiedzmy oba module1i module2miej zmienną jako b. Kiedy robię:

from module1 import *
from module2 import *

print b  # will print the value from module2

Tutaj wartość z module1zostaje utracona. Trudno będzie debugować, dlaczego kod nie działa, nawet jeśli bjest zadeklarowany wmodule1 a napisałem kod oczekując, że mój kod będzie używanymodule1.b

Jeśli masz te same zmienne w różnych modułach i nie chcesz importować całego modułu, możesz nawet zrobić:

from module1 import b as mod1b
from module2 import b as mod2b

2

W ramach testu utworzyłem moduł test.py z 2 funkcjami A i B, które wypisują odpowiednio „A 1” i „B 1”. Po zaimportowaniu test.py z:

import test

. . . Mogę uruchomić dwie funkcje jako test.A () i test.B (), a „test” pojawia się jako moduł w przestrzeni nazw, więc jeśli edytuję test.py, mogę go ponownie załadować za pomocą:

import importlib
importlib.reload(test)

Ale jeśli wykonam następujące czynności:

from test import *

nie ma odniesienia do „test” w przestrzeni nazw, więc nie ma możliwości ponownego załadowania go po edycji (o ile wiem), co jest problemem w sesji interaktywnej. Mając na uwadze, że jedno z poniższych:

import test
import test as tt

doda „test” lub „tt” (odpowiednio) jako nazwy modułów w przestrzeni nazw, co pozwoli na ponowne załadowanie.

Jeśli zrobię:

from test import *

nazwy „A” i „B” pojawiają się w przestrzeni nazw jako funkcje . Jeśli edytuję test.py i powtórzę powyższe polecenie, zmodyfikowane wersje funkcji nie zostaną ponownie załadowane.

Poniższe polecenie wywołuje komunikat o błędzie.

importlib.reload(test)    # Error - name 'test' is not defined

Jeśli ktoś wie jak przeładować moduł załadowany poleceniem „from module import *”, napisz. W przeciwnym razie byłby to kolejny powód, aby unikać formularza:

from module import *

2

Jak sugerowano w dokumentacji, (prawie) nigdy nie powinieneś używać import *w kodzie produkcyjnym.

Podczas gdy importowanie *z modułu jest złe, importowanie * z pakietu jest jeszcze gorsze. Domyślnie from package import *importuje wszystkie nazwy zdefiniowane przez pakiet __init__.py, w tym wszystkie podmoduły pakietu, które zostały załadowane przez poprzednie importinstrukcje.

Jeśli jednak __init__.pykod pakietu definiuje listę o nazwie __all__, przyjmuje się, że jest to lista nazw podmodułów, które powinny zostać zaimportowane po from package import *napotkaniu.

Rozważ ten przykład (zakładając, że nie ma __all__zdefiniowanej w sound/effects/__init__.py):

# anywhere in the code before import *
import sound.effects.echo
import sound.effects.surround

# in your module
from sound.effects import *

Ostatnia instrukcja zaimportuje moduły echoi surrounddo bieżącej przestrzeni nazw (prawdopodobnie nadpisując poprzednie definicje), ponieważ są one zdefiniowane w sound.effectspakiecie podczas wykonywania importinstrukcji.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.