Warum ist "Import *" schlecht?


152

Es wird empfohlen, nicht import *in Python zu verwenden.

Kann jemand bitte den Grund dafür mitteilen, damit ich es beim nächsten Mal vermeiden kann?



2
Es hängt davon ab, ob Sie Skripte erstellen oder Code schreiben, den Sie wiederverwenden müssen. Manchmal lohnt es sich, Codestandards zu ignorieren. "import *" kann auch in Ordnung sein, wenn Sie eine Namenskonvention haben, die klar macht, woher das Material stammt. zB "aus Katzenimport *; TabbyCat; MaineCoonCat; CalicoCat;"
Gatoatigrado

3
import *funktioniert nicht für mich in Python 2 oder 3.
Joshreesjones

1
Beantwortet das deine Frage? Was genau importiert "import *"?
AMC

Antworten:


222
  • Weil es eine Menge Dinge in Ihren Namespace bringt (möglicherweise ein anderes Objekt aus dem vorherigen Import beschattet und Sie nichts davon wissen).

  • Weil Sie nicht genau wissen, was importiert wird, und nicht leicht finden können, von welchem ​​Modul eine bestimmte Sache importiert wurde (Lesbarkeit).

  • Weil Sie keine coolen Tools verwenden können pyflakes, um Fehler in Ihrem Code statisch zu erkennen.


2
Ja, ich hasse es wirklich, wenn jemand * import verwendet, weil ich dann nicht einfach Pyflakes ausführen und glücklich sein kann, sondern diese Importe reparieren muss. Es ist aber schön, dass mit diesen Pyflakes mir hilft :-)
gruszczy

6
Als konkretes Beispiel wurden viele Benutzer von NumPy von numpy.anySchatten gebissen , anywenn sie dies tun, from numpy import *oder ein "hilfreiches" Tool erledigt dies für sie.
user2357112 unterstützt Monica

1
Sollte ich aus den gleichen Gründen vermeiden, den Schalter --pylab für IPython zu verwenden?
Timgeb

6
Um ein Risiko hervorzuheben, an das ich vor dem Lesen noch nie gedacht hatte ("könnte ein anderes Objekt vom vorherigen Import beschatten"): Macht import *die Reihenfolge der importAnweisungen signifikant ... selbst für Standardbibliotheksmodule, denen die Importreihenfolge normalerweise egal ist . Etwas so Unschuldiges wie das Alphabetisieren Ihrer importAussagen könnte Ihr Drehbuch brechen, wenn ein ehemaliges Opfer des Importkrieges der einzige Überlebende wird. (Selbst wenn Ihr Skript jetzt funktioniert und sich nie ändert, kann es später plötzlich fehlschlagen, wenn das importierte Modul einen neuen Namen einführt, der einen ersetzt, auf den Sie sich verlassen haben.)
Kevin J. Chase

48

Nach dem Zen von Python :

Explizit ist besser als implizit.

... kann man damit sicher nicht streiten?


28
Eigentlich kann man damit streiten. Es ist auch völlig inkonsistent, da Sie Variablen in Python nicht explizit deklarieren, sondern erst dann entstehen, wenn Sie sie zuweisen.
Konrad Rudolph

7
@gruszczy: Das Deklarieren von Variablen ist für was redundant ? Zuweisen? Nein, das sind zwei getrennte Konzepte, und die Erklärung von etwas vermittelt eine sehr eindeutige und wichtige Information. Wie auch immer, explizite Aussagen sind immer etwas mit Redundanz verbunden, sie sind zwei Gesichter derselben Medaille.
Konrad Rudolph

3
@kriss richtig, aber das war nicht mein Punkt. Mein Punkt war, dass das Versäumnis, eine Variable explizit zu deklarieren, zu Fehlern führt. Sie sagen, dass "eine Zuordnung ohne [Erklärung] unmöglich ist". Aber das ist falsch, mein springender Punkt ist, dass Python leider genau das möglich macht.
Konrad Rudolph

3
@kriss Eine weitere Information, die der Dekiler durch die Deklaration erhält, ist die Tatsache, dass Sie tatsächlich beabsichtigen, eine neue Variable zu deklarieren. Das ist eine wichtige Information für das Typensystem. Sie sagen, dass moderne IDEs das Problem der Tippfehler lösen, aber das ist einfach falsch, und tatsächlich ist dies ein erhebliches Problem in nicht statisch kompilierten Sprachen, weshalb Perl use strict(JavaScript var) hinzugefügt hat . Abgesehen davon ist Python natürlich nicht typenlos (es ist tatsächlich stark typisiert). Selbst wenn Sie Recht hätten, würde dies dem in dieser Antwort zitierten Zen von Python widersprechen.
Konrad Rudolph

3
@kriss Sie haben es falsch: die gleiche Variable Wiederverwendung Name ist kein Problem - die gleiche Wiederverwendung Variable ist (dh dem gleichen Namen im gleichen Umfang). Explizite Deklarationen würden genau diesen Fehler verhindern (und andere, basierend auf einfachem Tippfehler, der, wie gesagt, tatsächlich ein äußerst häufiges und zeitaufwändiges Problem ist, obwohl Sie Recht haben, dass das Problem in Perl-ähnlich größer ist Sprachen). Und der Widerspruch, auf den ich anspreche, ist das Erfordernis des Zen zur Erklärung, das hier körperlich aus dem Fenster geworfen wird.
Konrad Rudolph

40

Sie gehen nicht **locals()auf Funktionen über, oder?

Da Python keine "include" -Anweisung hat und der selfParameter explizit ist und die Gültigkeitsbereichsregeln recht einfach sind, ist es normalerweise sehr einfach, mit dem Finger auf eine Variable zu zeigen und festzustellen, woher dieses Objekt stammt - ohne andere Module zu lesen und ohne irgendeine der IDE (die ohnehin in der Art der Selbstbeobachtung begrenzt sind, weil die Sprache sehr dynamisch ist).

Das import *bricht alles.

Es gibt auch eine konkrete Möglichkeit, Fehler zu verbergen.

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

Wenn das Balkenmodul eines der Attribute " os", " mystuff" usw. hat, überschreiben sie die explizit importierten Attribute und verweisen möglicherweise auf sehr unterschiedliche Dinge. Das Definieren __all__in Balken ist oft sinnvoll - dies gibt an, was implizit importiert wird -, aber es ist immer noch schwierig zu verfolgen, woher Objekte kommen, ohne das Balkenmodul zu lesen und zu analysieren und seine Importe zu verfolgen . Ein Netzwerk von import *ist das erste, was ich repariere, wenn ich die Verantwortung für ein Projekt übernehme.

Versteht mich nicht falsch: Wenn die import *fehlen würden, würde ich weinen, um sie zu haben. Aber es muss vorsichtig verwendet werden. Ein guter Anwendungsfall besteht darin, eine Fassadenschnittstelle über einem anderen Modul bereitzustellen. Ebenso erfordert die Verwendung von bedingten Importanweisungen oder Importen in Funktions- / Klassennamensräumen ein wenig Disziplin.

Ich denke, bei mittelgroßen bis großen Projekten oder kleinen Projekten mit mehreren Mitwirkenden ist ein Minimum an Hygiene in Bezug auf die statische Analyse erforderlich - mindestens Pyflakes oder besser noch ein ordnungsgemäß konfigurierter Pylint -, um mehrere Arten von Fehlern zu erkennen sie passieren.

Da dies Python ist - zögern Sie nicht, Regeln zu brechen und zu erkunden -, aber seien Sie vorsichtig bei Projekten, die sich verzehnfachen könnten. Wenn dem Quellcode die Disziplin fehlt, ist dies ein Problem.


6
Python 2.x hat eine "include" -Anweisung. Es heißt execfile(). Glücklicherweise wird es selten verwendet und ist in 3.x verschwunden.
Sven Marnach

Wie wäre es **vars(), Globals einzuschließen, wenn sich die aufgerufene Funktion in einer anderen Datei befindet? : P
Solomon Ucko

16

Es ist in Ordnung, from ... import *in einer interaktiven Sitzung zu tun .


Wie wäre es in einer doctestSchnur? Wird das import *in diesem Fall in einer "Sandbox" interpretiert? Vielen Dank.
PatrickT

16

Das liegt daran, dass Sie den Namespace verschmutzen. Sie importieren alle Funktionen und Klassen in Ihren eigenen Namespace, was zu Konflikten mit den von Ihnen selbst definierten Funktionen führen kann.

Darüber hinaus denke ich, dass die Verwendung eines qualifizierten Namens für die Wartungsaufgabe klarer ist. Sie sehen in der Codezeile selbst, woher eine Funktion stammt, sodass Sie die Dokumente viel einfacher auschecken können.

Im Modul foo:

def myFunc():
    print 1

In Ihrem Code:

from foo import *

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

def myFunc():
    print 2


9

Angenommen, Sie haben den folgenden Code in einem Modul namens foo:

import ElementTree as etree

und dann haben Sie in Ihrem eigenen Modul:

from lxml import etree
from foo import *

Sie haben jetzt ein schwer zu debuggendes Modul, das so aussieht, als ob es lxml's etree enthält, aber stattdessen wirklich ElementTree.


7

Das sind alles gute Antworten. Ich werde hinzufügen, dass der Umgang mit Code in Python import *sehr schwierig ist , wenn man neuen Leuten das Codieren beibringt . Selbst wenn Sie oder sie den Code nicht geschrieben haben, ist er immer noch ein Stolperstein.

Ich bringe Kindern (ungefähr 8 Jahre alt) bei, in Python zu programmieren, um Minecraft zu manipulieren. Ich möchte ihnen eine hilfreiche Codierungsumgebung geben, mit der sie arbeiten können ( Atom Editor ) und die REPL-gesteuerte Entwicklung (über bpython ) lehren . In Atom finde ich, dass die Hinweise / Vervollständigungen genauso effektiv funktionieren wie bpython. Glücklicherweise lässt sich Atom im Gegensatz zu einigen anderen statistischen Analysewerkzeugen nicht täuschen import *.

Allerdings läßt dieses Beispiel nehmen ... In dieser Wrapper sie from local_module import *ein Bündel Module einschließlich dieser Liste von Blöcken . Ignorieren wir das Risiko von Namespace-Kollisionen. Auf from mcpi.block import *diese Weise machen sie diese gesamte Liste der obskuren Arten von Blöcken zu etwas, das Sie sich ansehen müssen, um zu wissen, was verfügbar ist. Wenn sie stattdessen verwendet hätten from mcpi import block, könnten Sie eingeben, walls = block.und dann würde eine Autovervollständigungsliste angezeigt. Atom.io Screenshot


6

Verstanden die gültigen Punkte, die die Leute hier setzen. Ich habe jedoch ein Argument, dass "Sternimport" manchmal nicht immer eine schlechte Praxis ist:

  • Wenn ich meinen Code so strukturieren möchte, dass alle Konstanten zu einem Modul namens aufgerufen werden const.py :
    • Wenn ich das tue import const, muss ich es für jede Konstante als bezeichnen const.SOMETHING, was wahrscheinlich nicht der bequemste Weg ist.
    • Wenn ich das tue from const import SOMETHING_A, SOMETHING_B ..., dann ist es offensichtlich viel zu ausführlich und macht den Zweck der Strukturierung zunichte.
    • Daher denke ich, dass es in diesem Fall from const import *eine bessere Wahl sein kann, a zu tun .

4

Es ist aus zwei Gründen eine sehr schlechte Praxis:

  1. Lesbarkeit des Codes
  2. Risiko des Überschreibens der Variablen / Funktionen usw.

Zu Punkt 1 : Sehen wir uns ein Beispiel dafür an:

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

a = b + c - d

Hier auf dem Sehen Sie den Code nicht wird eine Idee in Bezug auf von dem Modul erhalten b, cundd tatsächlich gehört.

Auf der anderen Seite, wenn Sie es mögen:

#                   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

Es ist viel sauberer für Sie und auch die neue Person, die Ihrem Team beitritt, hat eine bessere Idee.

Zu Punkt 2 : Sagen wir beides module1und module2haben die Variable als b. Wenn ich es tue:

from module1 import *
from module2 import *

print b  # will print the value from module2

Hier geht der Wert von module1verloren. Es ist schwer zu debuggen, warum der Code nicht funktioniert, selbst wenn er bin deklariert istmodule1 und ich den Code geschrieben habe, der die Verwendung meines Codes erwartetmodule1.b

Wenn Sie dieselben Variablen in verschiedenen Modulen haben und nicht das gesamte Modul importieren möchten, können Sie sogar Folgendes tun:

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

2

Als Test habe ich ein Modul test.py mit 2 Funktionen A und B erstellt, die jeweils "A 1" und "B 1" drucken. Nach dem Importieren von test.py mit:

import test

. . . Ich kann die beiden Funktionen als test.A () und test.B () ausführen, und "test" wird als Modul im Namespace angezeigt. Wenn ich also test.py bearbeite, kann ich es neu laden mit:

import importlib
importlib.reload(test)

Aber wenn ich folgendes mache:

from test import *

Es gibt keinen Verweis auf "test" im Namespace, daher gibt es keine Möglichkeit, ihn nach einer Bearbeitung neu zu laden (soweit ich das beurteilen kann), was in einer interaktiven Sitzung ein Problem darstellt. Während eine der folgenden:

import test
import test as tt

fügt "test" bzw. "tt" als Modulnamen in den Namespace ein, wodurch ein erneutes Laden möglich ist.

Wenn ich mache:

from test import *

Die Namen "A" und "B" werden im Namespace als Funktionen angezeigt . Wenn ich test.py bearbeite und den obigen Befehl wiederhole, werden die geänderten Versionen der Funktionen nicht neu geladen.

Der folgende Befehl löst eine Fehlermeldung aus.

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

Wenn jemand weiß, wie man ein mit "from module import *" geladenes Modul neu lädt, bitte posten. Andernfalls wäre dies ein weiterer Grund, das Formular zu vermeiden:

from module import *

2

Wie in den Dokumenten vorgeschlagen, sollten Sie (fast) niemals import *im Produktionscode verwenden.

Während der Import *aus einem Modul schlecht ist, ist der Import * aus einem Paket noch schlechter. Standardmäßig werden from package import *alle Namen importiert, die von den Paketen definiert wurden __init__.py, einschließlich aller Submodule des Pakets, die von den vorherigen geladen wurdenimport Anweisungen .

Wenn der __init__.pyCode eines Pakets jedoch eine Liste mit dem Namen definiert __all__, wird davon ausgegangen, dass es sich um die Liste der Submodulnamen handelt, die importiert werden sollen, wenn sie from package import *auftreten.

Betrachten Sie dieses Beispiel (vorausgesetzt, es ist nicht __all__definiert in 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 *

Die letzte Anweisung importiert die Module echound surroundin den aktuellen Namespace (möglicherweise werden vorherige Definitionen überschrieben), da sie sound.effectsbei der importAusführung der Anweisung im Paket definiert werden .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.