Frage

PEP 08 Zustände:

Importe werden immer oben in der Datei platziert, direkt nach Modulkommentaren und Dokumentzeichenfolgen und vor Modulglobalen und -konstanten.

Wenn die Klasse/Methode/Funktion, die ich importiere, jedoch nur in seltenen Fällen verwendet wird, ist es dann doch effizienter, den Import dann durchzuführen, wenn er benötigt wird?

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()
War es hilfreich?

Lösung

Modul einge ist recht schnell, aber nicht sofort. Dies bedeutet, dass:

  • Setzt man die Importe an der Oberseite des Moduls ist in Ordnung, weil es eine triviale Kosten ist, die nur einmal bezahlt wird.
  • innerhalb einer Funktion, die Einfuhren Einlochen verursacht werden Anrufe an diese Funktion länger dauern.

Wenn Sie also über Effizienz kümmern, an der Spitze die Importe setzen. Nur sie in eine Funktion bewegen, wenn der Profiler zeigt, dass helfen würde (Sie hat Profil zu sehen, wo die beste Leistung zu verbessern, oder ??)


Die besten Gründe, die ich gesehen habe faul Importe auszuführen sind:

  • Optional Bibliothek Unterstützung. Wenn Ihr Code mehr Pfade hat, die verschiedenen Bibliotheken verwenden, nicht brechen, wenn eine optionale Bibliothek nicht installiert ist.
  • Im __init__.py eines Plugins, die importiert werden können, aber nicht tatsächlich verwendet wird. Beispiele sind Bazaar Plugins, die bzrlib faule Lade Framework verwenden.

Andere Tipps

Inbetriebnahme der Import-Anweisung innerhalb einer Funktion kann zirkuläre Abhängigkeiten verhindern. wenn Sie 2 Module haben zum Beispiel, X.py und Y.py, und sie müssen beide einander importieren, wird dies eine zirkuläre Abhängigkeit verursachen, wenn Sie eines der Module eine Endlosschleife importieren verursacht. Wenn Sie die Import-Anweisung in einem der Module bewegen, dann wird es nicht versuchen, das andere Modul, bis die Funktion importieren wird aufgerufen, und das Modul wird bereits importiert werden, so dass keine Endlosschleife. Lesen Sie hier mehr - effbot.org/zone/import-confusion.htm

ich angenommen habe die Praxis alle Importe in den Funktionen setzen, die sie verwenden, anstatt an der Oberseite des Moduls.

Der Vorteil, den ich bekommen, ist die Fähigkeit, zuverlässiger Refactoring. Wenn ich eine Funktion von einem Modul zum anderen zu bewegen, weiß ich, dass die Funktion mit all seinen Erbe der Prüfung intakt arbeiten wird fortgesetzt. Wenn ich meine Einfuhren an der Oberseite des Moduls haben, wenn ich eine Funktion bewegen, finde ich, dass ich viel Zeit am Ende Ausgaben des neuen Moduls bekommen Importe vollständig und minimal. Ein Refactoring IDE könnte dies irrelevant machen.

Es gibt eine Geschwindigkeitseinbuße, wie an anderer Stelle erwähnt. Ich habe dies gemessen in meiner Anwendung und fand es für meine Zwecke unbedeutend.

Es ist auch schön, dass man alle Modulabhängigkeiten vorne sehen, ohne (zum Beispiel grep) suchen greifen zu müssen. Allerdings ist der Grund, warum ich über Modulabhängigkeiten Pflege im Allgemeinen, weil ich die Installation, Refactoring oder ein ganzes System, das mehrere Dateien bewegen, nicht nur ein einzelnes Modul. In diesem Fall werde ich auf jeden Fall eine globale Suche durchführen, um sicherzustellen, dass ich die System-Level-Abhängigkeiten. So habe ich nicht Weltimport gefunden mein Verständnis eines Systems in der Praxis zu unterstützen.

Ich habe in der Regel den Import von sys innerhalb der if __name__=='__main__' Prüfung und dann Argumente übergeben (wie sys.argv[1:]) zu einer main() Funktion. Dies ermöglicht es mir main in einem Kontext zu verwenden, in denen sys wurde nicht importiert.

Die meiste Zeit würde dies für Klarheit und sinnvoll sinnvoll sein, zu tun, aber es ist nicht immer der Fall. Unten sind ein paar Beispiele von Fällen, in denen Modulimporte anderswo leben könnten.

Zum einen könnten Sie ein Modul mit einem Unit-Test der Form haben:

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

Zweitens, könnten Sie eine Anforderung haben bedingt etwas anderes Modul zur Laufzeit importieren.

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

Es gibt wahrscheinlich auch andere Situationen, in denen Sie die Einfuhren in anderen Teilen im Code platzieren könnten.

Die erste Variante ist in der Tat effizienter ist als die zweite, wenn die Funktion entweder Null oder Eins-mal aufgerufen. Mit den zweiten und nachfolgenden Anrufungen jedoch die „Import jeden Anruf“ -Ansatz ist eigentlich weniger effizient. Siehe diesen Link für eine Technik, faul-Laden, die das Beste aus beiden vereint nähert sie durch einen "lazy Import" zu tun.

Aber es gibt andere Gründe als die Effizienz, warum Sie einen über den anderen vorziehen könnten. Ein Ansatz ist, macht es viel mehr klar, jemand den Code in Bezug auf die Abhängigkeiten zu lesen, die dieses Modul hat. Sie haben auch sehr unterschiedliche Ausfallverhalten - die erste zur Ladezeit wird scheitern, wenn es keine „Datetime“ -Modul ist, während der zweite wird nicht fehlschlagen, bis die Methode aufgerufen wird

.

hinzugefügt. Hinweis: In Ironpython, die Einfuhren um einiges teurer als in CPython sein können, weil der Code im Grunde kompiliert wird, wie es importiert werden wird

Curt macht einen guten Punkt: die zweite Version ist klarer und wird beim Laden nicht eher als später, und unerwartet

. Ich mach dir keine Sorgen über die Effizienz der Lademodule

Normalerweise, da es (a) ziemlich schnell, und (b) meist geschieht nur beim Start.

Wenn Sie Schwergewichts-Module zu unerwarteten Zeiten laden haben, macht es wahrscheinlich mehr Sinn, sie mit der __import__ Funktion dynamisch zu laden, und seinen sicher ImportError Ausnahmen fangen und verarbeiten sie in angemessener Weise .

Ich würde mir keine allzu großen Sorgen um die Effizienz machen, das Modul im Voraus zu laden.Der vom Modul beanspruchte Speicher wird nicht sehr groß sein (vorausgesetzt, es ist modular genug) und die Startkosten werden vernachlässigbar sein.

In den meisten Fällen möchten Sie die Module oben in der 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 einer anderen Stelle im Code zu importieren, besteht darin, dass es in einer Debugging-Anweisung verwendet wird.

Zum Beispiel:

do_something_with_x(x)

Ich könnte das 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 gut wie keine Wahl haben.

Ich würde mir keine allzu großen Sorgen um die Effizienz machen, das Modul im Voraus zu laden.Der vom Modul beanspruchte Speicher wird nicht sehr groß sein (vorausgesetzt, es ist modular genug) und die Startkosten werden vernachlässigbar sein.

Es ist ein Kompromiss, dass nur der Programmierer zu machen entscheiden.

Fall 1 spart etwas Speicher und Startzeit durch nicht das Datetime-Modul einge (und zu tun, was die Initialisierung es erforderlich machen könnten), bis sie benötigt. Beachten Sie, dass die Einfuhr tun ‚nur dann, wenn genannten‘ bedeutet auch, es zu tun ‚jedes Mal, wenn sie aufgerufen wird‘, so dass jeder Anruf nach dem ersten wird entstehen noch den zusätzlichen Aufwand für den Import zu tun.

Fall 2 eine Ausführungszeit und Latenz spart Datumzeit im Voraus, so dass not_often_called Import () wird schneller zurück, wenn es ist genannt, und auch nicht bei jedem Aufruf den Aufwand einer Einfuhr entstehen.

Neben Effizienz, ist es einfache Modulabhängigkeiten vorne zu sehen, ob die Import-Anweisungen sind ... vorne. Verstecken sie in den Code unten kann es schwieriger machen leicht zu finden, was etwas Module abhängt.

Persönlich folge ich im Allgemeinen dem PEP außer für Dinge wie Unit-Tests und so, dass ich will nicht immer geladen, weil ich weiß sie werden nicht außer für Testcode verwendet werden.

Hier ist ein Beispiel, wo alle Einfuhren an der Spitze sind (dies ist die einzige Zeit, die ich gebraucht habe, dies zu tun). Ich möchte in der Lage, eine subprocess sowohl Un * x und Windows zu beenden.

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

(On Bewertung: Was

Das ist wie viele andere Optimierungen - Sie einige Lesbarkeit für Geschwindigkeit opfern. Wie John erwähnt, wenn Sie Ihre Hausaufgaben und Profilierung gefunden gemacht habe dies eine deutlich nützlich genug Änderung und sein, Sie die zusätzliche Geschwindigkeit benötigen, dann gehen sie. Es wäre wahrscheinlich gut sein, eine Note zu setzen mit allen anderen Importen:

from foo import bar
from baz import qux
# Note: datetime is imported in SomeClass below

Modul Initialisierung erfolgt nur einmal - auf dem ersten Import. Wenn das betreffende Modul aus der Standardbibliothek ist, dann werden Sie wahrscheinlich auch von anderen Modulen in Ihrem Programm importieren. Für ein Modul so weit verbreitet wie Datetime ist es auch wahrscheinlich eine Abhängigkeit für eine ganze Reihe von anderen Standardbibliotheken. Die Import-Anweisung würde sehr wenig kostet dann, da das Modul intialization bereits geschehen wäre. das vorhandene Modul ist verbindlich Objekt an den lokalen Bereich Alles, was es an dieser Stelle zu tun.

Paar, dass Informationen mit dem Argument, um die Lesbarkeit und ich würde sagen, dass es am besten ist es, die Import-Anweisung im Modul Umfang zu haben.

Just Moe Antwort und die ursprüngliche Frage abzuschließen:

Wenn wir mit Kreis Abhängigkeiten zu tun haben, können wir einige „Tricks“ tun. Unter der Annahme, wir arbeiten mit Modulen a.py und b.py die x() und b y() enthalten sind. Dann gilt:

  1. Wir können an der Unterseite des Moduls eine der from imports bewegen.
  2. Wir können innerhalb der Funktion oder Methode eines der from imports bewegen, die tatsächlich den Import ist erforderlich (dies ist nicht immer möglich, wie man es von mehreren Stellen verwenden).
  3. Wir können eine der beiden from imports ändern ein Import zu sein, die wie folgt aussieht: import a

Also, zu schließen. Wenn Sie nicht mit zirkulären Abhängigkeiten und machen eine Art von Trick zu tun, sie zu vermeiden, dann ist es besser, alle Ihre Importe an der Spitze zu setzen, weil der Gründe, die bereits in anderen Antworten auf diese Frage erklärt. Und bitte, wenn diese „Tricks“ zu tun umfassen einen Kommentar, es ist immer willkommen! :)

Neben den hervorragenden Antworten schon gegeben, ist es erwähnenswert, dass die Platzierung der Einfuhren ist nicht nur eine Frage des Stils. Manchmal hat ein Modul implizite Abhängigkeiten, die zuerst importiert werden oder initialisiert müssen, und ein Top-Level-Import zu Verletzungen der erforderlichen Reihenfolge der Ausführung führen könnte.

Dieses Problem kommt oft in Apache Spark Python API, in dem Sie die SparkContext initialisieren müssen, bevor Sie pyspark Pakete oder Module zu importieren. Am besten ist es pyspark Einfuhren in einem Umfang zu platzieren, wo der SparkContext garantiert zur Verfügung steht.

Ich strebe keine vollständige Antwort zu geben, weil andere dies bereits sehr gut getan haben. Ich möchte nur einen Anwendungsfall erwähnen, wenn ich besonders nützlich finde Module innerhalb von Funktionen zu importieren. Meine Anwendung verwendet Python-Pakete und als Plug-in in bestimmtem Ort gespeichert Module. Während Start der Anwendung, die Anwendung aller Module in der Lage geht durch und importiert sie, dann sieht es im Innern der Module und wenn es einige Befestigungspunkte für die Plugins findet (in meinem Fall ist es eine Unterklasse von einer bestimmten Basisklasse einen eindeutigen mit ID) es registriert sie. Die Anzahl des Plugins ist groß (jetzt Dutzende, aber vielleicht Hunderte in der Zukunft) und jeder von ihnen ist ziemlich selten verwendet. Mit der Einfuhr von Drittanbieter-Bibliotheken an der Spitze meines Plugin-Module ein bisschen Strafe beim Anwendungsstart wurde. Insbesondere sind einige Bibliotheken von Drittanbietern schwer zu importieren (zum Beispiel Import von plotly sogar eine Verbindung zum Internet versucht und etwas herunterladen, die etwa eine Sekunde lang wurde die Zugabe der Inbetriebnahme). Durch Importe Optimierung (nennt sie nur in den Funktionen, in denen sie verwendet werden) in dem Plugins, die ich den Start von 10 Sekunden bis etwa 2 Sekunden schrumpfen verwaltet. Das ist ein großer Unterschied für meine Nutzer.

Also meine Antwort ist nein, nicht immer die Importe an der Spitze Ihrer Module setzen.

Ich war überrascht, nicht tatsächlich Kosten Zahlen für die wiederholten Last-Kontrollen bereits gebucht zu sehen, obwohl es gibt viele gute Erklärungen, was zu erwarten ist.

Wenn Sie an der Spitze zu importieren, nehmen Sie die Last treffen, egal was. Das ist ziemlich klein, aber häufig in den Millisekunden, nicht ns.

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 haben darauf hingewiesen, wenn das überhaupt nicht passieren, können Sie die Ladezeit sparen. Aber wenn die Funktion (en) viel aufgerufen, nehmen Sie ein, obwohl viel kleiner Hit wiederholt (für die Überprüfung, dass es hat geladen wurde, nicht für tatsächlich Nachladen). Auf der anderen Seite, wie @aaronasterling wies darauf hin, sparen Sie auch ein wenig, weil innerhalb einer Funktion zu importieren die Verwendung Funktion kann leicht schnelleren lokale Variable Lookups den Namen identifizieren später (http://stackoverflow.com/questions/477096/python-import-coding-style/4789963#4789963 ).

Hier sind die Ergebnisse eines einfachen Test, der ein paar Dinge aus dem Inneren einer Funktion importiert. Die Zeiten berichteten (in Python 2.7.14 auf einem 2,3 GHz Intel Core i7) sind unten gezeigt (der zweite Anruf nimmt mehr als später Anrufe konsistent scheint, obwohl ich weiß nicht, 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

Es ist interessant, dass nicht eine einzige Antwort Parallelverarbeitung bisher genannten, wo es erforderlich sein könnte, dass die Einfuhren in der Funktion sind, wenn der serialisierten Funktionscode ist, was um auf andere Kerne geschoben wird z.B. wie im Fall von ipyparallel.

Es kann durch den Import von Variablen / local Scoping innerhalb einer Funktion eine Leistungssteigerung sein. Dies hängt von der Nutzung der importierten Sache innerhalb der Funktion. Wenn Sie viele Male und den Zugriff auf ein Modul globales Objekt werden Looping, es als lokale Import helfen können.

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 auf Linux zeigt eine kleine Verstärkung

/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

real ist Wanduhr. Benutzer Zeit in Programm. sys ist Zeit für Systemaufrufe.

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

würde Ich mag eine usecase von mir erwähnen, sehr ähnlich derjenigen, die von @ John Millikin und @ V.K. :

Optional Import

ich Datenanalyse mit Jupyter Notebook, und ich verwende die gleiche IPython Notebook als Vorlage für alle Analysen. In einigen Fällen muss ich Tensorflow importieren einige schnelle Modell läuft, zu tun, aber manchmal arbeite ich an Orten, wo tensorflow nicht eingerichtet ist / ist langsam zu importieren. In diesen Fällen kapseln ich meine Tensorflow-abhängige Operationen in einer Hilfsfunktion, Import tensorflow innerhalb dieser Funktion, und binden Sie es an eine Schaltfläche.

Auf diese Weise konnte ich tun „Neustart-and-run-all“ ohne für den Import zu warten, oder mit dem Rest der Zellen wieder aufzunehmen, wenn es fehlschlägt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top