Sollten Importanweisungen immer ganz oben in einem Modul stehen?

403

In PEP 08 heißt es:

Importe werden immer am Anfang der Datei platziert, direkt nach Modulkommentaren und Dokumentzeichenfolgen sowie vor Modulglobalen und -konstanten.

Wenn jedoch die Klasse / Methode / Funktion, die ich importiere, nur in seltenen Fällen verwendet wird, ist es sicherlich effizienter, den Import bei Bedarf durchzuführen?

Ist das nicht:

class SomeClass(object):

    def not_often_called(self)
        from datetime import datetime
        self.datetime = datetime.now()

effizienter als das?

from datetime import datetime

class SomeClass(object):

    def not_often_called(self)
        self.datetime = datetime.now()
Adam J. Forster
quelle

Antworten:

283

Der Modulimport ist recht schnell, aber nicht sofort. Das bedeutet, dass:

  • Es ist in Ordnung, die Importe oben im Modul zu platzieren, da es sich um geringfügige Kosten handelt, die nur einmal bezahlt werden.
  • Wenn Sie die Importe in eine Funktion einfügen, dauert der Aufruf dieser Funktion länger.

Wenn Sie also Wert auf Effizienz legen, setzen Sie die Importe an die Spitze. Verschieben Sie sie nur in eine Funktion, wenn Ihre Profilerstellung dies zeigt (Sie haben ein Profil erstellt, um zu sehen, wo die Leistung am besten verbessert werden kann, oder?)


Die besten Gründe, warum ich faule Importe durchgeführt habe, sind:

  • Optionale Bibliotheksunterstützung. Wenn Ihr Code mehrere Pfade enthält, die unterschiedliche Bibliotheken verwenden, brechen Sie nicht, wenn keine optionale Bibliothek installiert ist.
  • In __init__.pyeinem Plugin, das möglicherweise importiert, aber nicht verwendet wird. Beispiele sind Bazaar-Plugins, die bzrlibdas Lazy-Loading-Framework verwenden.
John Millikin
quelle
17
John, das war eine völlig theoretische Frage, also hatte ich keinen Code zum Profilieren. In der Vergangenheit bin ich immer dem PEP gefolgt, aber ich habe heute einen Code geschrieben, der mich gefragt hat, ob das das Richtige ist. Danke für Ihre Hilfe.
Adam J. Forster
43
> Wenn Sie die Importe in eine Funktion einfügen, dauert der Aufruf dieser Funktion länger. Eigentlich denke ich, dass diese Kosten nur einmal bezahlt werden. Ich habe gelesen, dass Python ein importiertes Modul zwischenspeichert, sodass der erneute Import nur minimale Kosten verursacht.
geschmolzene Form
24
@halfhourhacks Python importiert das Modul nicht erneut, muss jedoch noch einige Anweisungen ausführen, um festzustellen, ob das Modul in sys.modules / etc. vorhanden ist / ist.
John Millikin
24
-1. Das Einfügen von Importen in eine Funktion führt nicht unbedingt dazu, dass sie länger dauert. Bitte sehen Sie meine Antwort auf eine andere Frage.
Aaronasterling
4
Ein Anwendungsfall ist die Vermeidung von Zirkularimporten (normalerweise nicht sinnvoll, aber manchmal geeignet). Manchmal ruft Klasse A in Modul m1 eine Methode für Klasse B in Modul m2 auf, die eine andere Instanz von Klasse A erstellt. Wenn für die Methode in Klasse B, die eine Instanz von Klasse A erstellt, der Import nur ausgeführt wird, wenn die Funktion ausgeführt wird, die eine Instanz erstellt, Der zirkuläre Import wird vermieden.
Sam Svenbjorgchristiensensen
80

Das Einfügen der import-Anweisung in eine Funktion kann zirkuläre Abhängigkeiten verhindern. Wenn Sie beispielsweise zwei Module haben, X.py und Y.py, und beide sich gegenseitig importieren müssen, führt dies zu einer zirkulären Abhängigkeit, wenn Sie eines der Module importieren, was eine Endlosschleife verursacht. Wenn Sie die import-Anweisung in eines der Module verschieben, wird nicht versucht, das andere Modul zu importieren, bis die Funktion aufgerufen wird, und dieses Modul wird bereits importiert, also keine Endlosschleife. Lesen Sie hier mehr - effbot.org/zone/import-confusion.htm

Moe
quelle
3
Ja, aber man kann in die Abhängigkeitshölle geraten.
Eigenein
8
Wenn sich zwei Module gegenseitig importieren müssen, stimmt ein schwerwiegender Fehler mit dem Code nicht.
Anna
Objektorientierte Programmierung führt mich oft zu zirkulären Abhängigkeiten. Eine wichtige Objektklasse kann in mehrere Module importiert werden. Damit dieses Objekt seine eigenen Aufgaben ausführen kann, muss es möglicherweise eines oder mehrere dieser Module erreichen. Es gibt Möglichkeiten, dies zu vermeiden, z. B. das Senden von Daten über Funktionsargumente an das Objekt, damit es auf das andere Modul zugreifen kann. Aber es gibt Zeiten, in denen sich dies für OOP sehr kontraintuitiv anfühlt (die Außenwelt sollte nicht wissen müssen, wie sie die Aufgabe in dieser Funktion erfüllt).
Robert
4
Wenn X Y braucht und Y X braucht, sind sie entweder zwei Teile derselben Idee (dh sollten zusammen definiert werden) oder es fehlt eine Abstraktion.
GLRoman
59

Ich habe die Praxis übernommen, alle Importe in die Funktionen zu setzen, die sie verwenden, und nicht oben im Modul.

Der Vorteil, den ich bekomme, ist die Fähigkeit, zuverlässiger umzugestalten. Wenn ich eine Funktion von einem Modul in ein anderes verschiebe, weiß ich, dass die Funktion weiterhin funktioniert, wenn das gesamte Erbe des Testens intakt bleibt. Wenn ich meine Importe oben im Modul habe und eine Funktion verschiebe, verbringe ich viel Zeit damit, die Importe des neuen Moduls vollständig und minimal zu machen. Eine Refactoring-IDE kann dies irrelevant machen.

Es gibt eine Geschwindigkeitsstrafe, wie an anderer Stelle erwähnt. Ich habe dies in meiner Bewerbung gemessen und festgestellt, dass es für meine Zwecke unbedeutend ist.

Es ist auch schön, alle Modulabhängigkeiten im Voraus sehen zu können, ohne auf die Suche zurückgreifen zu müssen (z. B. grep). Der Grund, warum ich mich für Modulabhängigkeiten interessiere, ist im Allgemeinen, dass ich ein gesamtes System mit mehreren Dateien installiere, umgestalte oder verschiebe, nicht nur ein einzelnes Modul. In diesem Fall werde ich trotzdem eine globale Suche durchführen, um sicherzustellen, dass ich die Abhängigkeiten auf Systemebene habe. Daher habe ich keine globalen Importe gefunden, um mein Verständnis eines Systems in der Praxis zu verbessern.

Normalerweise setze ich den Import von sysin die if __name__=='__main__'Prüfung und übergebe dann Argumente (wie sys.argv[1:]) an eine main()Funktion. Dies ermöglicht mir die Verwendung mainin einem Kontext, in sysden nicht importiert wurde.


quelle
4
Viele IDEs vereinfachen das Refactoring von Code, indem sie die erforderlichen Module für Sie optimieren und automatisch in Ihre Datei importieren. In den meisten Fällen haben PyCharm und Eclipse die richtigen Entscheidungen für mich getroffen. Ich würde wetten, dass es eine Möglichkeit gibt, das gleiche Verhalten bei Emacs oder Vim zu erreichen.
brent.payne
3
Ein Import innerhalb einer if-Anweisung im globalen Namespace ist weiterhin ein globaler Import. Dadurch werden die Argumente gedruckt (mit Python 3): def main(): print(sys.argv); if True: import sys; main();Sie müssten if __name__=='__main__'eine Funktion einschließen , um einen neuen Namespace zu erstellen.
Darcinon
4
Dies scheint mir ein hervorragender Grund zu sein, innerhalb von Funktionen und nicht im globalen Bereich zu importieren. Ich bin ziemlich überrascht, dass niemand anderes dies aus demselben Grund erwähnt hat. Gibt es neben Leistung und Ausführlichkeit auch erhebliche Nachteile?
Alge
@algal der Nachteil ist, dass viele Python-Leute dies hassen, weil Sie den Pep-Codex verletzen. Sie müssen Ihre Teammitglieder überzeugen. Der Leistungsverlust ist minimal. Manchmal ist es sogar noch schneller, siehe stackoverflow.com/a/4789963/362951
mit
Ich fand es äußerst nützlich für das Refactoring, Importe nahe an den Ort zu bringen, an dem ich sie verwende. Es ist nicht mehr notwendig, so viele Tims nach oben und hinten zu scrollen. Ich benutze IDEs wie Pycharm oder Wing Ide und benutze auch deren Refactoring, aber ich möchte mich nicht immer auf sie verlassen. Das Verschieben von Funktionen in ein anderes Modul wird mit diesem alternativen Importstil viel einfacher. Infolgedessen überarbeite ich viel mehr.
Mit
39

Meistens wäre dies aus Gründen der Klarheit nützlich und sinnvoll, aber dies ist nicht immer der Fall. Im Folgenden finden Sie einige Beispiele für Umstände, unter denen Modulimporte möglicherweise an anderer Stelle ausgeführt werden.

Erstens könnten Sie ein Modul mit einem Komponententest des Formulars haben:

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

Zweitens müssen Sie möglicherweise zur Laufzeit ein anderes Modul bedingt importieren.

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

Es gibt wahrscheinlich andere Situationen, in denen Sie möglicherweise Importe in andere Teile des Codes einfügen.

ConcernedOfTunbridgeWells
quelle
14

Die erste Variante ist in der Tat effizienter als die zweite, wenn die Funktion entweder null oder einmal aufgerufen wird. Mit dem zweiten und den folgenden Aufrufen ist der Ansatz "Jeden Anruf importieren" jedoch weniger effizient. Siehe diesen Link eine Technik zum verzögerten Laden, bei der das Beste aus beiden Ansätzen durch einen "verzögerten Import" kombiniert wird.

Es gibt jedoch andere Gründe als die Effizienz, warum Sie einen dem anderen vorziehen könnten. Ein Ansatz besteht darin, jemandem, der den Code liest, viel klarer zu machen, welche Abhängigkeiten dieses Modul aufweist. Sie haben auch sehr unterschiedliche Fehlereigenschaften - das erste schlägt beim Laden fehl, wenn kein "datetime" -Modul vorhanden ist, während das zweite erst nach dem Aufruf der Methode ausfällt.

Hinweis hinzugefügt: In IronPython können Importe erheblich teurer sein als in CPython, da der Code beim Importieren im Grunde genommen kompiliert wird.

Curt Hagenlocher
quelle
1
Es ist nicht wahr, dass der erste besser abschneidet
Jason Baker
Es ist besser, wenn die Methode nie aufgerufen wird, da der Import nie stattfindet.
Curt Hagenlocher
Stimmt, aber es funktioniert schlechter, wenn die Methode mehr als einmal aufgerufen wird. Die Leistungsvorteile, die Sie erzielen würden, wenn Sie das Modul nicht sofort importieren, sind in den meisten Fällen vernachlässigbar. Die Ausnahme wäre, wenn das Modul sehr groß ist oder es viele solcher Funktionen gibt.
Jason Baker
In der IronPython-Welt sind Erstimporte viel teurer als in CPython;). Das Beispiel "Lazy Import" in Ihrem Link ist wahrscheinlich die beste generische Gesamtlösung.
Curt Hagenlocher
Ich hoffe es macht dir nichts aus, aber ich habe das in deinem Beitrag bearbeitet. Das sind hilfreiche Informationen.
Jason Baker
9

Curt macht einen guten Punkt: Die zweite Version ist klarer und wird eher beim Laden als später und unerwartet ausfallen.

Normalerweise mache ich mir keine Sorgen um die Effizienz des Ladens von Modulen, da es (a) ziemlich schnell ist und (b) meistens nur beim Start geschieht.

Wenn Sie Schwergewichts - Module zu unerwarteten Zeiten laden haben, macht es wahrscheinlich mehr Sinn , sie mit der dynamischen zu Laden - __import__Funktion und seine sicher zu fangen ImportErrorAusnahmen, und sie in angemessener Weise zu behandeln.

Dan Lenski
quelle
8

Ich würde mir keine Sorgen über die Effizienz machen, das Modul im Voraus zu stark zu laden. Der vom Modul belegte Speicher wird nicht sehr groß sein (vorausgesetzt, er ist modular genug) und die Startkosten sind vernachlässigbar.

In den meisten Fällen möchten Sie die Module oben in die Quelldatei laden. Für jemanden, der Ihren Code liest, ist es viel einfacher zu erkennen, welche Funktion oder welches Objekt von welchem ​​Modul stammt.

Ein guter Grund, ein Modul an eine andere Stelle im Code zu importieren, ist die Verwendung in einer Debugging-Anweisung.

Zum Beispiel:

do_something_with_x(x)

Ich könnte dies debuggen mit:

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

Der andere Grund, Module an anderer Stelle im Code zu importieren, besteht natürlich darin, dass Sie sie dynamisch importieren müssen. Das liegt daran, dass Sie so ziemlich keine Wahl haben.

Ich würde mir keine Sorgen über die Effizienz machen, das Modul im Voraus zu stark zu laden. Der vom Modul belegte Speicher wird nicht sehr groß sein (vorausgesetzt, er ist modular genug) und die Startkosten sind vernachlässigbar.

Jason Baker
quelle
Wir sprechen von zehn Millisekunden Startkosten pro Modul (auf meinem Computer). Dies ist nicht immer vernachlässigbar, z. B. wenn es die Reaktionsfähigkeit einer Webanwendung auf einen Benutzerklick beeinträchtigt.
Evgeni Sergeev
6

Es ist ein Kompromiss, den nur der Programmierer treffen kann.

Fall 1 spart Speicherplatz und Startzeit, indem das datetime-Modul nicht importiert wird (und die erforderliche Initialisierung durchgeführt wird), bis es benötigt wird. Beachten Sie, dass der Import "nur beim Aufruf" auch "jedes Mal beim Aufruf" bedeutet, sodass jeder Aufruf nach dem ersten immer noch den zusätzlichen Aufwand für den Import verursacht.

Fall 2 spart einige Ausführungszeit und Latenz, indem Sie datetime vorher importieren, damit not_often_called () schneller zurückkehrt, wenn dies der Fall ist genannt, und auch nicht bei jedem Aufruf den Aufwand einer Einfuhr entstehen.

Neben der Effizienz ist es einfacher, Modulabhängigkeiten im Voraus zu erkennen, wenn die Importanweisungen im Vordergrund stehen. Wenn Sie sie im Code ausblenden, kann es schwieriger werden, leicht zu finden, von welchen Modulen etwas abhängt.

Persönlich folge ich im Allgemeinen dem PEP, mit Ausnahme von Unit-Tests und solchen, die ich nicht immer laden möchte, weil ich weiß , dass sie nur für Testcode verwendet werden.

pjz
quelle
2
-1. Der Hauptaufwand beim Importieren tritt nur beim ersten Mal auf. Die Kosten für das sys.modulesNachschlagen des Moduls können leicht durch die Einsparungen ausgeglichen werden, da nur ein lokaler Name anstelle eines globalen Namens gesucht werden muss .
Aaronasterling
6

Hier ist ein Beispiel, in dem alle Importe ganz oben stehen (dies ist das einzige Mal, dass ich dies tun muss). Ich möchte einen Unterprozess sowohl unter Un * x als auch unter Windows beenden können.

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

(Zur Überprüfung: Was John Millikin gesagt hat.)

vergoldet
quelle
6

Dies ist wie bei vielen anderen Optimierungen - Sie opfern eine gewisse Lesbarkeit für die Geschwindigkeit. Wie John bereits erwähnt hat, sollten Sie Ihre Profiling-Hausaufgaben erledigen und feststellen, dass dies eine ausreichend nützliche Änderung ist und Sie die zusätzliche Geschwindigkeit benötigen. Es wäre wahrscheinlich gut, eine Notiz über alle anderen Importe zu machen:

from foo import bar
from baz import qux
# Note: datetime is imported in SomeClass below
Drew Stephens
quelle
4

Die Modulinitialisierung erfolgt nur einmal - beim ersten Import. Wenn das betreffende Modul aus der Standardbibliothek stammt, werden Sie es wahrscheinlich auch aus anderen Modulen in Ihrem Programm importieren. Für ein Modul, das so häufig wie datetime ist, ist es wahrscheinlich auch eine Abhängigkeit für eine Reihe anderer Standardbibliotheken. Die Importanweisung würde dann sehr wenig kosten, da die Modulinitialisierung bereits stattgefunden hätte. Zu diesem Zeitpunkt wird lediglich das vorhandene Modulobjekt an den lokalen Bereich gebunden.

Kombinieren Sie diese Informationen mit dem Argument für die Lesbarkeit, und ich würde sagen, dass es am besten ist, die import-Anweisung im Modulbereich zu haben.

Jeremy Brown
quelle
4

Nur um Moes Antwort zu vervollständigen und die ursprüngliche Frage :

Wenn wir uns mit zirkulären Abhängigkeiten befassen müssen, können wir einige "Tricks" machen. Angenommen, wir arbeiten mit Modulen a.pyund b.pydiese enthalten x()bzw. b y(). Dann:

  1. Wir können einen der from importsam unteren Rand des Moduls verschieben.
  2. Wir können eine from importsder Funktionen oder Methoden verschieben, für die der Import tatsächlich erforderlich ist (dies ist nicht immer möglich, da Sie sie möglicherweise von mehreren Stellen aus verwenden).
  3. Wir können einen der beiden ändern from imports, um einen Import zu erhalten, der wie folgt aussieht:import a

Also zum Schluss. Wenn Sie sich nicht mit zirkulären Abhängigkeiten befassen und einen Trick ausführen, um sie zu vermeiden, ist es aus den Gründen, die bereits in anderen Antworten auf diese Frage erläutert wurden, besser, alle Importe an die Spitze zu setzen. Und bitte, wenn diese "Tricks" einen Kommentar enthalten, ist es immer willkommen! :) :)

Caumons
quelle
4

Neben den bereits gegebenen hervorragenden Antworten ist anzumerken, dass die Platzierung von Importen nicht nur eine Frage des Stils ist. Manchmal weist ein Modul implizite Abhängigkeiten auf, die zuerst importiert oder initialisiert werden müssen, und ein Import auf oberster Ebene kann zu Verstößen gegen die erforderliche Ausführungsreihenfolge führen.

Dieses Problem tritt häufig in der Python-API von Apache Spark auf, wo Sie den SparkContext initialisieren müssen, bevor Sie Pyspark-Pakete oder -Module importieren. Es ist am besten, Pyspark-Importe in einem Bereich zu platzieren, in dem der SparkContext garantiert verfügbar ist.

Paul
quelle
4

Ich war überrascht, dass die tatsächlichen Kostenzahlen für die wiederholten Ladeprüfungen nicht bereits veröffentlicht wurden, obwohl es viele gute Erklärungen dafür gibt, was zu erwarten ist.

Wenn Sie oben importieren, nehmen Sie den Lasttreffer, egal was passiert. Das ist ziemlich klein, aber normalerweise in Millisekunden, nicht in Nanosekunden.

Wenn Sie innerhalb einer Funktion (en) zu importieren, dann nehmen Sie nur den Treffer zum Laden , wenn und wenn eine dieser Funktionen wird zuerst genannt. Wie viele darauf hingewiesen haben, sparen Sie die Ladezeit, wenn dies überhaupt nicht geschieht. Aber wenn die Funktion (en) viel aufgerufen, nehmen Sie ein , obwohl viel kleiner Hit wiederholt (für die Überprüfung , dass es wurde geladen worden ist , nicht für tatsächlich Nachladen). Andererseits sparen Sie, wie @aaronasterling hervorhob, auch ein wenig, da beim Importieren innerhalb einer Funktion die Funktion etwas schnellere lokale Variablensuchen verwendet , um den Namen später zu identifizieren ( http://stackoverflow.com/questions/477096/python-). Import-Coding-Stil / 4789963 # 4789963 ).

Hier sind die Ergebnisse eines einfachen Tests, bei dem einige Dinge aus einer Funktion importiert werden. Die angegebenen Zeiten (in Python 2.7.14 auf einem Intel Core i7 mit 2,3 GHz) sind unten aufgeführt (der zweite Anruf, der mehr als spätere Anrufe entgegennimmt, scheint konsistent zu sein, obwohl ich nicht weiß, warum).

 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

Der Code:

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
quelle
Die Änderungen in der Laufzeit sind wahrscheinlich auf die Skalierung der CPU-Frequenz als Reaktion auf die Last zurückzuführen. Es ist besser, Geschwindigkeitstests mit einer Sekunde Arbeit zu beginnen, um die CPU-Taktrate zu erhöhen.
Han-Kwang Nienhuys
3

Ich strebe keine vollständige Antwort an, da andere dies bereits sehr gut gemacht haben. Ich möchte nur einen Anwendungsfall erwähnen, wenn ich das Importieren von Modulen in Funktionen besonders nützlich finde. Meine Anwendung verwendet Python-Pakete und -Module, die an einem bestimmten Ort als Plugins gespeichert sind. Während des Anwendungsstarts durchläuft die Anwendung alle Module am Speicherort und importiert sie. Anschließend werden die Module überprüft und es werden einige Einhängepunkte für die Plugins gefunden (in meinem Fall handelt es sich um eine Unterklasse einer bestimmten Basisklasse mit einer eindeutigen ID) registriert sie. Die Anzahl der Plugins ist groß (jetzt Dutzende, aber vielleicht Hunderte in der Zukunft) und jedes von ihnen wird ziemlich selten verwendet. Der Import von Bibliotheken von Drittanbietern oben auf meinen Plugin-Modulen war beim Start der Anwendung eine kleine Strafe. Insbesondere einige Bibliotheken von Drittanbietern sind schwer zu importieren (z. B. der Import von Plotly versucht sogar, eine Verbindung zum Internet herzustellen und etwas herunterzuladen, das dem Start etwa eine Sekunde hinzufügte). Durch die Optimierung der Importe (die nur in den Funktionen aufgerufen werden, in denen sie verwendet werden) in den Plugins konnte ich den Start von 10 Sekunden auf etwa 2 Sekunden verkürzen. Das ist ein großer Unterschied für meine Benutzer.

Meine Antwort lautet also nein, setzen Sie die Importe nicht immer ganz oben auf Ihre Module.

VK
quelle
3

Es ist interessant, dass bisher keine einzige Antwort die Parallelverarbeitung erwähnte, bei der möglicherweise ERFORDERLICH ist, dass die Importe in der Funktion enthalten sind, wenn der serialisierte Funktionscode auf andere Kerne übertragen wird, z. B. wie bei ipyparallel.

K.-Michael Aye
quelle
1

Durch den Import von Variablen / lokalem Gültigkeitsbereich innerhalb einer Funktion kann ein Leistungsgewinn erzielt werden. Dies hängt von der Verwendung des importierten Objekts innerhalb der Funktion ab. Wenn Sie viele Male eine Schleife ausführen und auf ein globales Modulobjekt zugreifen, kann es hilfreich sein, es als lokal zu importieren.

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

Eine Zeit unter Linux zeigt einen kleinen Gewinn

/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

echt ist wanduhr. Benutzer ist Zeit im Programm. sys ist Zeit für Systemaufrufe.

https://docs.python.org/3.5/reference/executionmodel.html#resolution-of-names

quiet_penguin
quelle
1

Lesbarkeit

Zusätzlich zur Startleistung muss ein Lesbarkeitsargument für die Lokalisierung von importAnweisungen angegeben werden. Nehmen Sie zum Beispiel die Python-Zeilennummern 1283 bis 1296 in meinem aktuellen ersten Python-Projekt:

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

Wenn die importAussage oben in der Datei wäre, müsste ich weit nach oben scrollen oder drücken Home, um herauszufinden, was ist ET. Dann müsste ich zurück zu Zeile 1283 navigieren, um den Code weiterzulesen.

Selbst wenn sich die importAnweisung an der Spitze der Funktion (oder Klasse) befindet, wie viele sie platzieren würden, wäre ein Auf- und Abblättern erforderlich.

Das Anzeigen der Gnome-Versionsnummer wird selten durchgeführt, sodass importoben in der Datei unnötige Startverzögerungen auftreten.

WinEunuuchs2Unix
quelle
0

Ich möchte einen meiner Fälle erwähnen, der den von @John Millikin und @VK erwähnten sehr ähnlich ist:

Optionale Importe

Ich mache Datenanalysen mit Jupyter Notebook und verwende dasselbe IPython-Notebook als Vorlage für alle Analysen. In einigen Fällen muss ich Tensorflow importieren, um einige schnelle Modellläufe durchzuführen, aber manchmal arbeite ich an Orten, an denen Tensorflow nicht eingerichtet ist / nur langsam importiert wird. In diesen Fällen kapsle ich meine Tensorflow-abhängigen Operationen in eine Hilfsfunktion, importiere Tensorflow in diese Funktion und binde sie an eine Schaltfläche.

Auf diese Weise konnte ich "Neustart-and-Run-All" durchführen, ohne auf den Import warten zu müssen oder den Rest der Zellen wieder aufnehmen zu müssen, wenn dies fehlschlägt.

Zeder
quelle
0

Dies ist eine faszinierende Diskussion. Wie viele andere hatte ich dieses Thema noch nie in Betracht gezogen. Ich musste die Importe in den Funktionen haben, weil ich das Django ORM in einer meiner Bibliotheken verwenden wollte. Ich musste django.setup()vor dem Importieren meiner Modellklassen aufrufen und da dies oben in der Datei war, wurde es aufgrund der IoC-Injektorkonstruktion in einen vollständig nicht-Django-Bibliothekscode gezogen.

Ich habe ein bisschen herumgehackt und am Ende django.setup()den Singleton-Konstruktor und den entsprechenden Import an die Spitze jeder Klassenmethode gesetzt. Jetzt funktionierte das gut, machte mich aber unruhig, weil die Importe nicht an der Spitze standen und ich mir auch Sorgen über die zusätzliche Zeit der Importe machte. Dann bin ich hierher gekommen und habe mit großem Interesse gelesen, dass alle dies annehmen.

Ich habe einen langen C ++ - Hintergrund und verwende jetzt Python / Cython. Ich gehe davon aus, dass Sie die Importe nicht in die Funktion einfügen, es sei denn, dies führt zu einem profilierten Engpass. Es ist nur so, als würden Sie Platz für Variablen deklarieren, bevor Sie sie benötigen. Das Problem ist, dass ich Tausende von Codezeilen mit allen Importen oben habe! Also denke ich, ich werde es von jetzt an tun und die ungerade Datei hier und da ändern, wenn ich durch bin und die Zeit habe.

LJHW
quelle