2017-01-05 2 views
7

Thesetwo Fragen betreffen Verwendung von import innerhalb einer Funktion vs. an der Spitze eines Moduls. Ich muss nicht überzeugt werden, meine Importe an die Spitze zu setzen, dafür gibt es gute Gründe. Um jedoch die technischen Probleme besser zu verstehen, möchte ich einen Follow-up bitten.Können Sie Importe in Funktionen mit Verschlüssen optimieren?

Können Sie das Beste aus beiden Welten performancetechnisch erzielen, indem Sie einen Verschluss verwenden und nur beim ersten Start der Funktion importieren?


Insbesondere nehme an, Sie Code haben, wie:

import sys 
def get_version(): 
    return sys.version 

Sie auf den Import wollen nur dann geschehen, wenn die Funktion immer aufgerufen wird, so dass man es im Inneren bewegen:

def get_version(): 
    import sys 
    return sys.version 

Aber jetzt ist es langsam, wenn es ruft wird viel aufgerufen, so dass Sie etwas komplexer versuchen:

Jetzt zeigt zumindest eine grundlegende Performance-Test, dass diese letzte Option etwas langsamer ist als die erste (dauert ~ 110% so lang), aber viel schneller als die zweite (~ 20% so lang).


Erstens, funktioniert das tatsächlich? Zeigen meine Messungen genau, dass das zweite Beispiel mehr Arbeit leistet oder ein Artefakt dafür ist, wie ich die Dinge gemessen habe?

Zweitens, gibt es eine Verlangsamung von der Schließung - über das erste Mal, dass die Funktion ausgeführt wird?

+0

Sie können den 'lru_cache'-Dekorator von' functools' verwenden, wenn es sich um eine Peer-Funktion handelt. Nicht sicher, ob es eine gute Idee ist oder nicht, aber es würde das Ergebnis der Funktion nach dem ersten Mal zwischenspeichern. – sytech

+1

@sytech: Der 'lru_cache'-Cache-Test ist ziemlich schwer, ich bezweifle, dass er für etwas so Leichtes wie einen Import (der nach der anfänglichen Dateiladung nichts anderes ist, als einen Namen zu binden) eine Menge Leistung bringt. –

Antwort

3

Closure dereferencing ist nicht schneller als die globale Lookups:

>>> import sys 
>>> sys.version_info 
sys.version_info(major=3, minor=6, micro=0, releaselevel='final', serial=0) 
>>> from timeit import timeit 
>>> glob = 'foo' 
>>> def f1(): return glob 
... 
>>> def closure(): 
...  closed_over = 'bar' 
...  def f2(): 
...   return closed_over 
...  return f2 
... 
>>> f2 = closure() 
>>> timeit(f1, number=10**7) 
0.8623221110319719 
>>> timeit(f2, number=10**7) 
0.872071701916866 

Darüber hinaus, auch wenn es schneller war, der Kompromiss gegen die Lesbarkeit ist es nicht wert, schon gar nicht, wenn schneller gewählt werden, wenn Sie müssen wirklich den Code optimieren.

Die Einheimischen sind die schnellste Option, immer, wenn Sie wirklich Code aus einer engen Schleife, die richtige Hybrid-Standardwerte verwendet Funktionsargument genannt müssen optimieren:

import sys.version 

def get_version(_sys_version=sys.version): 
    return _sys_version 

Wenn Sie sich mit den Auswirkungen der betroffen sind Erstes Laden der Datei von einem Import zum Zeitpunkt des Starts, vielleicht sollten Sie stattdessen die py-demandimport project betrachten, die Lademodule verschiebt, bis sie zum ersten Mal verwendet werden.

+1

Ja, wie ich in der Frage geschrieben habe, zeigten meine Messungen an, dass es etwas langsamer war. Der Punkt bestand darin, die Geschwindigkeit zu optimieren und gleichzeitig die Nur-Import-Wenn-Eigenschaft beizubehalten, die in Ihrem Funktionsargumentbeispiel fehlt. – otus

+0

Das war nur hypothetisch, aber dieses py-demandimport-Projekt scheint etwas zu sein, das ich tatsächlich benutzen könnte. Vielen Dank. – otus

Verwandte Themen