Python-Import-Codierungsstil

69

Ich habe ein neues Muster entdeckt. Ist dieses Muster bekannt oder wie ist die Meinung dazu?

Grundsätzlich fällt es mir schwer, Quelldateien nach oben und unten zu scrubben, um herauszufinden, welche Modulimporte verfügbar sind, und so weiter

import foo
from bar.baz import quux

def myFunction():
    foo.this.that(quux)

Ich verschiebe alle meine Importe in die Funktion, in der sie tatsächlich verwendet werden.

def myFunction():
    import foo
    from bar.baz import quux

    foo.this.that(quux)

Dies macht ein paar Dinge. Erstens verschmutzte ich meine Module selten versehentlich mit dem Inhalt anderer Module. Ich könnte die __all__Variable für das Modul festlegen , aber dann müsste ich sie aktualisieren, wenn sich das Modul weiterentwickelt, und das hilft nicht bei der Verschmutzung des Namespaces für Code, der tatsächlich im Modul lebt.

Zweitens habe ich selten eine Litanei von Importen am oberen Rand meiner Module, von denen ich die Hälfte oder mehr nicht mehr benötige, weil ich sie überarbeitet habe. Schließlich finde ich dieses Muster VIEL einfacher zu lesen, da jeder referenzierte Name genau dort im Funktionskörper ist.

TokenMacGuy
quelle

Antworten:

123

Die (zuvor) am besten gewählte Antwort auf diese Frage ist gut formatiert, aber in Bezug auf die Leistung absolut falsch. Lassen Sie mich demonstrieren

Performance

Top Import

import random

def f():
    L = []
    for i in xrange(1000):
        L.append(random.random())


for i in xrange(1000):
    f()

$ time python import.py

real        0m0.721s
user        0m0.412s
sys         0m0.020s

In Funktionskörper importieren

def f():
    import random
    L = []
    for i in xrange(1000):
        L.append(random.random())

for i in xrange(1000):
    f()

$ time python import2.py

real        0m0.661s
user        0m0.404s
sys         0m0.008s

Wie Sie sehen können, kann es sein , mehr effizient das Modul in der Funktion zu importieren. Der Grund dafür ist einfach. Es verschiebt die Referenz von einer globalen Referenz zu einer lokalen Referenz. Dies bedeutet, dass der Compiler zumindest für CPython LOAD_FASTAnweisungen anstelle von LOAD_GLOBALAnweisungen ausgibt. Diese sind, wie der Name schon sagt, schneller. Der andere Antwortende hat den Leistungseinbruch beim Einschauen künstlich erhöht, sys.modulesindem er bei jeder einzelnen Iteration der Schleife importiert hat .

In der Regel ist es am besten, oben zu importieren, aber die Leistung ist nicht der Grund, wenn Sie häufig auf das Modul zugreifen. Die Gründe dafür sind, dass man leichter verfolgen kann, wovon ein Modul abhängt, und dass dies mit dem größten Teil des restlichen Python-Universums übereinstimmt.

aaronasterling
quelle
6
+1 für die Referenzierung des tatsächlichen Bytecode-Unterschieds ... manchmal mache ich einige Klassenattribute in Methoden lokal, wenn ich sie sehr oft in der Funktion verwenden werde (sowohl sauberer Bytecode als auch sauberer Code)
lunixbochs
6
Der Import hat eine nicht triviale Strafe. Dies wird hier durch den lokalen Zugriff und das Vorhandensein einer Schleife in der Funktion verschleiert. Wenn Sie im Top-Import-Beispiel "r = random" hinzufügen und r.random () verwenden, erhalten Sie die gleiche Leistung wie beim zweiten. Wenn Sie "r = random.random" hinzufügen und "r ()" verwenden, wird es noch schneller.
H Krishnan
1
@ HKrishnan, ganz richtig. Ich war wohl nicht klar genug. Wenn häufig auf das Modul zugegriffen wird, ist der Import in die Funktion schneller. Was Sie vorschlagen, ist schneller, aber nur geringfügig, es sei denn, die importierte Funktion wird mehrmals aufgerufen. Insgesamt bin ich ein Befürworter des Imports auf Modulebene. Die einzige Situation, an die ich denken kann, wenn ich in die Funktion importiere, ist optimal, wenn nicht erwartet wird, dass die Funktion während der normalen Ausführung eines Programms aufgerufen wird und eindeutige Importe aufweist. Django Ansichten wären ein gutes Beispiel.
Aaronasterling
3
Wenn Sie nur LOAD_FASTaus Leistungsgründen import randomauf globaler Ebene möchten und dann den random_ = randomlokalen Bereich festlegen und verwenden random_(oder noch besser, speichern Sie einige Attributsuchen mit random_ = random.randomoder from random import random). Es LOAD_FASTist eine schlechte Idee, bei jedem Funktionsaufruf den Leistungseinbruch eines Imports zu essen, nur um ihn zu verwenden .
user2357112 unterstützt Monica
56

Dies hat einige Nachteile.

Testen

Wenn Sie Ihr Modul nicht durch Laufzeitänderungen testen möchten, wird es möglicherweise schwieriger. Anstatt zu tun

import mymodule
mymodule.othermodule = module_stub

Du musst tun

import othermodule
othermodule.foo = foo_stub

Dies bedeutet, dass Sie das andere Modul global patchen müssen, anstatt nur zu ändern, worauf die Referenz in mymodule verweist.

Abhängigkeitsverfolgung

Dies macht es nicht offensichtlich, von welchen Modulen Ihr Modul abhängt. Dies ist besonders ärgerlich, wenn Sie viele Bibliotheken von Drittanbietern verwenden oder Code neu organisieren.

Ich musste einen alten Code pflegen, der überall Inline-Importe verwendete. Dadurch war es äußerst schwierig, den Code umzugestalten oder neu zu verpacken.

Hinweise zur Leistung

Aufgrund der Art und Weise, wie Python Module zwischenspeichert, gibt es keinen Leistungseinbruch. Da sich das Modul im lokalen Namespace befindet, bietet der Import von Modulen in eine Funktion einen geringfügigen Leistungsvorteil.

Top Import

import random

def f():
    L = []
    for i in xrange(1000):
        L.append(random.random())

for i in xrange(10000):
    f()


$ time python test.py 

real   0m1.569s
user   0m1.560s
sys    0m0.010s

In Funktionskörper importieren

def f():
    import random
    L = []
    for i in xrange(1000):
        L.append(random.random())

for i in xrange(10000):
    f()

$ time python test2.py

real    0m1.385s
user    0m1.380s
sys     0m0.000s
Ryan
quelle
3
Vielleicht möchten Sie dies klarstellen - Importe werden jedes Mal überprüft, aber das Modul wird nur einmal geladen.
S.Lott
1
Danke für die Eingabe. selbst wenn Module zwischengespeichert werden, ist es noch nicht eine große Auswirkung auf die Leistung haben, wie Sie aus meinen Tests zu sehen.
Ryan
Ja, aber jetzt hast du es klar gemacht. Es war sehr irreführend. Meine negative Stimme wurde entfernt
nosklo
2
Kein gutes Beispiel, da Sie den Import in die for-Schleife und nicht nur in die Definition von f () einfügen. Aber im Allgemeinen hat der lokale Import Kosten.
Davidavr
1
@ Ryan -1. Wie ist es einfacher, die import-Anweisung innerhalb der Schleife einzugeben, als sie außerhalb einzugeben? Ihre Antwort ist absolut falsch in Bezug auf die Leistung. Bitte sehen Sie meine .
Aaronasterling
23

Einige Probleme mit diesem Ansatz:

  • Beim Öffnen der Datei ist nicht sofort ersichtlich, von welchen Modulen sie abhängt.
  • Es wird Programme verwirren , die Abhängigkeiten analysieren müssen, wie zum Beispiel py2exe, py2appusw.
  • Was ist mit Modulen, die Sie in vielen Funktionen verwenden? Sie werden entweder viele redundante Importe haben oder Sie müssen einige oben in der Datei und einige interne Funktionen haben.

Also ... der bevorzugte Weg ist, alle Importe oben in die Datei zu setzen. Ich habe festgestellt, dass wenn meine Importe schwer nachzuverfolgen sind, dies normalerweise bedeutet, dass ich zu viel Code habe, um ihn besser in zwei oder mehr Dateien aufzuteilen.

Einige Situationen , in denen ich habe die Einfuhren innerhalb von Funktionen gefunden , nützlich zu sein:

  • Um mit zirkulären Abhängigkeiten umzugehen (wenn Sie sie wirklich nicht wirklich vermeiden können)
  • Plattformspezifischer Code

Außerdem: Das Einfügen von Importen in jede Funktion ist tatsächlich nicht wesentlich langsamer als am Anfang der Datei. Wenn jedes Modul zum ersten Mal geladen wird, wird es abgelegt sys.modules, und jeder nachfolgende Import kostet nur die Zeit zum Nachschlagen des Moduls, was ziemlich schnell ist (es wird nicht neu geladen).

dF.
quelle
+1: Und es ist langsam. Jeder Funktionsaufruf muss die Überprüfung des Importmoduls wiederholen.
S.Lott
10

Ein weiterer nützlicher Hinweis ist, dass die from module import *Syntax innerhalb einer Funktion in Python 3.0 entfernt wurde.

Es gibt eine kurze Erwähnung unter "Entfernte Syntax" hier:

http://docs.python.org/3.0/whatsnew/3.0.html

Russell Bryant
quelle
1
-1: Falsch. Nur das Formular "from xxx import *" wurde für Funktionen deaktiviert.
Nosklo
6
Er sagte, dass Teile des Imports deaktiviert wurden. Seien Sie nicht so schnell dabei, Leute herunterzustimmen, die nützliche Informationen geben.
Daniel Naab
4

Ich würde vorschlagen, dass Sie versuchen, from foo import barImporte zu vermeiden . Ich verwende sie nur in Paketen, in denen die Aufteilung in Module ein Implementierungsdetail darstellt und es sowieso nicht viele davon gibt.

An allen anderen Stellen, an denen Sie ein Paket importieren, verwenden Sie es einfach import foound referenzieren Sie es mit dem vollständigen Namen foo.bar. Auf diese Weise können Sie immer erkennen, woher ein bestimmtes Element stammt, und müssen die Liste der importierten Elemente nicht pflegen (in Wirklichkeit ist dies immer veraltet und importiert nicht mehr verwendete Elemente).

Wenn fooes sich um einen wirklich langen Namen handelt, können Sie ihn vereinfachen import foo as fund dann schreiben f.bar. Dies ist immer noch weitaus bequemer und expliziter als die Verwaltung aller fromImporte.

Nikow
quelle
3

Die Leute haben sehr gut erklärt, warum Inline-Importe vermieden werden sollten, aber nicht wirklich alternative Workflows, um die Gründe anzusprechen, aus denen Sie sie überhaupt wollen.

Es fällt mir schwer, Quelldateien nach oben und unten zu scrubben, um herauszufinden, welche Modulimporte verfügbar sind und so weiter

Um nach nicht verwendeten Importen zu suchen, verwende ich Pylint . Es führt eine statische (ish) -Analyse von Python-Code durch, und eines der (vielen) Dinge, auf die es prüft, sind nicht verwendete Importe. Zum Beispiel das folgende Skript ..

import urllib
import urllib2

urllib.urlopen("http://stackoverflow.com")

..wurde die folgende Nachricht generieren:

example.py:2 [W0611] Unused import urllib2

Bei der Überprüfung der verfügbaren Importe verlasse ich mich im Allgemeinen auf die (ziemlich vereinfachte) Vervollständigung von TextMate. Wenn Sie die Esc-Taste drücken, wird das aktuelle Wort mit anderen im Dokument vervollständigt. Wenn ich fertig bin import urllib, urll[Esc]wird erweitert auf urllib, wenn nicht, springe ich zum Anfang der Datei und füge den Import hinzu.

dbr
quelle
3

Ich glaube, dass dies in einigen Fällen / Szenarien ein empfohlener Ansatz ist. In Google App Engine wird beispielsweise das verzögerte Laden großer Module empfohlen, da dadurch die Aufwärmkosten für die Instanziierung neuer Python-VMs / -Interpreter minimiert werden. Schauen Sie sich die Präsentation eines Google Engineer an, in der dies beschrieben wird. Beachten Sie jedoch, dass dies nicht bedeutet, dass Sie alle Module faul laden sollten.

fuentesjr
quelle
2

Unter dem Gesichtspunkt der Leistung können Sie Folgendes sehen: Sollten Python-Importanweisungen immer oben in einem Modul stehen?

Im Allgemeinen verwende ich nur lokale Importe, um Abhängigkeitszyklen zu unterbrechen.

Sykora
quelle
Ein Vorschlag: Unterbrechen Sie die Abhängigkeitszyklen, indem Sie alles, was beide Module benötigen, in ein drittes Modul einfügen. Lassen Sie beide Module dieses dritte importieren.
Nosklo
@nosklo: Ausgezeichneter Vorschlag. Es ist trivial, Abhängigkeitszyklen in Python durch Refactoring zu unterbrechen.
S.Lott
2

Beide Varianten haben ihre Verwendung. In den meisten Fällen ist es jedoch besser, außerhalb der Funktionen zu importieren, nicht innerhalb dieser.

Performance

Es wurde in mehreren Antworten erwähnt, aber meiner Meinung nach fehlt allen eine vollständige Diskussion.

Wenn ein Modul zum ersten Mal in einen Python-Interpreter importiert wird, ist es langsam, unabhängig davon, ob es sich in der obersten Ebene oder in einer Funktion befindet. Es ist langsam, weil Python (ich konzentriere mich auf CPython, es könnte für andere Python-Implementierungen anders sein) mehrere Schritte ausführt:

  • Findet das Paket.
  • Überprüft, ob das Paket bereits in Bytecode (das berühmte __pycache__Verzeichnis oder die .pyxDateien) konvertiert wurde, und wenn nicht, konvertiert es diese in Bytecode.
  • Python lädt den Bytecode.
  • Das geladene Modul wird eingelegt sys.modules.

Nachfolgende Importe müssen nicht alle diese Aufgaben ausführen, da Python das Modul einfach von zurückgeben kann sys.modules. So werden nachfolgende Importe viel schneller sein.

Es kann sein, dass eine Funktion in Ihrem Modul nicht sehr oft verwendet wird, dies hängt jedoch von einer Funktion ab import, die ziemlich lange dauert. Dann könnten Sie tatsächlich das importInnere der Funktion verschieben. Dies beschleunigt den Import Ihres Moduls (da das lange Ladepaket nicht sofort importiert werden muss). Wenn die Funktion jedoch endgültig verwendet wird, ist sie beim ersten Aufruf langsam (da das Modul dann importiert werden muss). Dies kann sich auf die wahrgenommene Leistung auswirken, da Sie nicht alle Benutzer verlangsamen, sondern nur diejenigen, die die Funktion verwenden, die von der Abhängigkeit vom langsamen Laden abhängt.

Die Suche in sys.modulesist jedoch nicht kostenlos. Es ist sehr schnell, aber nicht kostenlos. Wenn Sie also tatsächlich eine Funktion aufrufen, die ein importPaket ist, werden Sie eine leicht verschlechterte Leistung feststellen:

import random
import itertools

def func_1():
    return random.random()

def func_2():
    import random
    return random.random()

def loopy(func, repeats):
    for _ in itertools.repeat(None, repeats):
        func()

%timeit loopy(func_1, 10000)
# 1.14 ms ± 20.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit loopy(func_2, 10000)
# 2.21 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Das ist fast zweimal langsamer.

Es ist sehr wichtig zu erkennen, dass aaronasterling in der Antwort ein bisschen "betrogen" hat . Er erklärte, dass der Import in die Funktion die Funktion tatsächlich schneller macht. Und bis zu einem gewissen Grad ist dies wahr. Das liegt daran, wie Python nach Namen sucht:

  • Es überprüft zuerst den lokalen Bereich.
  • Als nächstes wird der umgebende Bereich überprüft.
  • Dann wird der nächste umgebende Bereich überprüft
  • ...
  • Der globale Bereich wird überprüft.

Anstatt den lokalen Bereich und dann den globalen Bereich zu überprüfen, reicht es aus, den lokalen Bereich zu überprüfen, da der Name des Moduls im lokalen Bereich verfügbar ist. Das macht es tatsächlich schneller! Aber das ist eine Technik namens "Schleifeninvariante Codebewegung" . Dies bedeutet im Grunde, dass Sie den Overhead von etwas reduzieren, das in einer Schleife (oder wiederholt) ausgeführt wird, indem Sie es in einer Variablen vor der Schleife (oder den wiederholten Aufrufen) speichern. Anstatt importes in der Funktion zu verwenden, können Sie auch einfach eine Variable verwenden und sie dem globalen Namen zuweisen:

import random
import itertools

def f1(repeats):
    "Repeated global lookup"
    for _ in itertools.repeat(None, repeats):
        random.random()

def f2(repeats):
    "Import once then repeated local lookup"
    import random
    for _ in itertools.repeat(None, repeats):
        random.random()

def f3(repeats):
    "Assign once then repeated local lookup"
    local_random = random
    for _ in itertools.repeat(None, repeats):
        local_random.random()

%timeit f1(10000)
# 588 µs ± 3.92 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit f2(10000)
# 522 µs ± 1.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit f3(10000)
# 527 µs ± 4.51 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Während Sie deutlich sehen können, dass wiederholte Suchvorgänge für das globale randomModul langsam sind, gibt es praktisch keinen Unterschied zwischen dem Importieren des Moduls in die Funktion oder dem Zuweisen des globalen Moduls in einer Variablen innerhalb der Funktion.

Dies könnte bis zum Äußersten gehen, indem auch die Funktionssuche innerhalb der Schleife vermieden wird:

def f4(repeats):
    from random import random
    for _ in itertools.repeat(None, repeats):
        random()

def f5(repeats):
    r = random.random
    for _ in itertools.repeat(None, repeats):
        r()

%timeit f4(10000)
# 364 µs ± 9.34 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit f5(10000)
# 357 µs ± 2.73 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Wieder viel schneller, aber es gibt fast keinen Unterschied zwischen dem Import und der Variablen.

Optionale Abhängigkeiten

Manchmal kann ein Import auf Modulebene tatsächlich ein Problem sein. Wenn Sie beispielsweise keine weitere Abhängigkeit von der Installationszeit hinzufügen möchten, das Modul jedoch für einige zusätzliche Funktionen sehr hilfreich ist. Die Entscheidung, ob eine Abhängigkeit optional sein soll, sollte nicht leichtfertig getroffen werden, da dies die Benutzer betrifft (entweder wenn sie unerwartet sind ImportErroroder auf andere Weise die "coolen Funktionen" verpassen) und die Installation des Pakets mit allen Funktionen für den Normalzustand komplizierter wird Abhängigkeiten pipoderconda (um nur zwei Paketmanager zu nennen) funktionieren sofort, aber für optionale Abhängigkeiten müssen die Benutzer Pakete später manuell installieren (es gibt einige Optionen, die es ermöglichen, die Anforderungen anzupassen, aber dann wieder die Last der Installation). richtig "wird auf den Benutzer gelegt).

Aber auch dies könnte auf beide Arten geschehen:

try:
    import matplotlib.pyplot as plt
except ImportError:
    pass

def function_that_requires_matplotlib():
    plt.plot()

oder:

def function_that_requires_matplotlib():
    import matplotlib.pyplot as plt
    plt.plot()

Dies könnte angepasst werden, indem alternative Implementierungen bereitgestellt oder die Ausnahme (oder Nachricht) angepasst werden, die der Benutzer sieht. Dies ist jedoch der Hauptinhalt.

Der Top-Level-Ansatz könnte etwas besser sein, wenn man eine alternative "Lösung" für die optionale Abhängigkeit bereitstellen möchte, jedoch wird im Allgemeinen der In-Function-Import verwendet. Meistens, weil es zu einer saubereren Stapelspur führt und kürzer ist.

Zirkuläre Importe

In-Function-Importe können sehr hilfreich sein, um ImportErrors aufgrund von zirkulären Importen zu vermeiden. In vielen Fällen sind zirkuläre Importe ein Zeichen für eine "schlechte" Paketstruktur, aber wenn es absolut keine Möglichkeit gibt, einen zirkulären Import zu vermeiden, werden die "Kreise" (und damit die Probleme) gelöst, indem die Importe, die zum Kreis führen, hineingelegt werden die Funktionen, die es tatsächlich verwenden.

Wiederhole dich nicht

Wenn Sie tatsächlich alle Importe in die Funktion anstelle des Modulbereichs einfügen, wird Redundanz eingeführt, da Funktionen wahrscheinlich dieselben Importe erfordern. Das hat ein paar Nachteile:

  • Sie haben jetzt mehrere Stellen, an denen Sie überprüfen können, ob ein Import veraltet ist.
  • Falls Sie einen Import falsch geschrieben haben, werden Sie dies erst herausfinden, wenn Sie die spezifische Funktion ausführen und nicht beim Laden. Da Sie mehr Importanweisungen haben, steigt die Wahrscheinlichkeit eines Fehlers (nicht viel) und es wird nur ein kleines bisschen wichtiger, alle Funktionen zu testen.

Zusätzliche Gedanken:

Am Ende meiner Module befindet sich selten eine Litanei von Importen, von denen die Hälfte oder mehr ich nicht mehr benötige, weil ich sie überarbeitet habe.

Die meisten IDEs verfügen bereits über einen Prüfer für nicht verwendete Importe, sodass dies wahrscheinlich nur ein paar Klicks sind, um sie zu entfernen. Selbst wenn Sie keine IDE verwenden, können Sie gelegentlich ein statisches Code-Überprüfungsskript verwenden und es manuell reparieren. Eine andere Antwort erwähnte Pylint, aber es gibt andere (zum Beispiel Pyflakes).

Ich verschmutzte meine Module selten versehentlich mit dem Inhalt anderer Module

Aus diesem Grund verwenden __all__und / oder definieren Sie normalerweise Ihre Funktionssubmodule und importieren nur die relevanten Klassen / Funktionen / ... in das Hauptmodul, z __init__.py.

Auch wenn Sie der Meinung sind, dass Sie den Modul-Namespace zu stark verschmutzt haben, sollten Sie wahrscheinlich in Betracht ziehen, das Modul in Submodule aufzuteilen. Dies ist jedoch nur für Dutzende von Importen sinnvoll.

Ein zusätzlicher (sehr wichtiger) Punkt, den Sie erwähnen sollten, wenn Sie die Verschmutzung durch Namespaces reduzieren möchten, ist die Vermeidung von from module import *Importen. Möglicherweise möchten Sie aber auch from module import a, b, c, d, e, ...Importe vermeiden , die zu viele Namen importieren, und einfach das Modul importieren und mit auf die Funktionen zugreifen module.c.

Als letzten Ausweg können Sie immer Aliase verwenden, um zu vermeiden, dass der Namespace mit "öffentlichen" Importen verschmutzt wird, indem Sie Folgendes verwenden : import random as _random. Dadurch wird der Code schwerer zu verstehen, aber es wird sehr deutlich, was öffentlich sichtbar sein sollte und was nicht. Ich würde es nicht empfehlen, Sie sollten nur die __all__Liste auf dem neuesten Stand halten (was der empfohlene und vernünftige Ansatz ist).

Zusammenfassung

  • Die Auswirkungen auf die Leistung sind sichtbar, werden jedoch fast immer mikrooptimiert. Lassen Sie sich also nicht von Mikro-Benchmarks leiten, wo Sie die Importe platzieren. Außer wenn die Abhängigkeit zuerst sehr langsam ist importund nur für einen kleinen Teil der Funktionalität verwendet wird. Dann kann es für die meisten Benutzer tatsächlich einen sichtbaren Einfluss auf die wahrgenommene Leistung Ihres Moduls haben.

  • Verwenden Sie die allgemein verständlichen Tools zum Definieren der öffentlichen API, ich meine die __all__Variable. Es mag etwas ärgerlich sein, es auf dem neuesten Stand zu halten, aber es überprüft auch alle Funktionen auf veraltete Importe oder wenn Sie eine neue Funktion hinzufügen, um alle relevanten Importe in dieser Funktion hinzuzufügen. Auf lange Sicht müssen Sie wahrscheinlich weniger Arbeit durch Aktualisierung erledigen __all__.

  • Es ist wirklich egal, welches Sie bevorzugen, beide arbeiten. Wenn Sie alleine arbeiten, können Sie über die Vor- und Nachteile nachdenken und das tun, was Sie für das Beste halten. Wenn Sie jedoch in einem Team arbeiten, sollten Sie sich wahrscheinlich an bekannte Muster halten (bei denen es sich um Importe auf höchster Ebene handelt __all__), da sie damit das tun können, was sie (wahrscheinlich) immer getan haben.

MSeifert
quelle
1

Vielleicht möchten Sie einen Blick auf den Overhead der Importanweisung im Python-Wiki werfen . Kurz gesagt: Wenn das Modul bereits geladen wurde (siehe sys.modules), wird Ihr Code langsamer ausgeführt. Wenn Ihr Modul noch nicht geladen wurde und foonur bei Bedarf geladen wird, was nullmal sein kann, ist die Gesamtleistung besser.

RSabet
quelle
2
-1 Der Code wird nicht unbedingt langsamer ausgeführt. Siehe meine Antwort .
Aaronasterling
-1

Sicherheitsimplementierungen

Stellen Sie sich eine Umgebung vor, in der sich Ihr gesamter Python-Code in einem Ordner befindet, auf den nur ein privilegierter Benutzer Zugriff hat. Um zu vermeiden, dass Ihr gesamtes Programm als privilegierter Benutzer ausgeführt wird, beschließen Sie, Berechtigungen während der Ausführung einem nicht privilegierten Benutzer zu übertragen. Sobald Sie eine Funktion verwenden, die ein anderes Modul importiert, löst Ihr Programm eine aus, ImportErrorda der nicht privilegierte Benutzer das Modul aufgrund von Dateiberechtigungen nicht importieren kann.

1 'ODER 1 -
quelle