Codeoptimierung Teil 1
Teilen Sie einige Tipps zur Codeoptimierung, die ich kürzlich gesehen habe.
Die Kurzschlusseigenschaft des if-Urteils
Für und sollten diejenigen, die die wenigsten Bedingungen erfüllen, zuerst platziert werden, sodass bei einer großen Anzahl von Urteilen die wenigen erfüllten Bedingungen vorhanden sind führt direkt zu anderen Ausdrücken, die folgen. Es wird nicht berechnet, um Zeit zu sparen (da False und True immer noch False sind).
import timeit s1 = """ a = range(2000) [i for i in a if i % 2 ==0 and i > 1900] """ s2 = """ a = range(2000) [i for i in a if i > 1900 and i % 2 ==0] """ print timeit.timeit(stmt=s1, number=1000) print timeit.timeit(stmt=s2, number=1000)
Die laufenden Ergebnisse sind wie folgt:
➜ python test6.py 0.248532056808 0.195827960968 # 可以看到s2 表达式计算更快, 因为大部分情况都不满足 i>1900, 所以这些情况下, i % 2 == 0 也没有计算,从而节约了时间
In ähnlicher Weise setzen Sie für oder diejenigen an die erste Stelle, die die meisten Bedingungen erfüllen.
import timeit s1 = """ a = range(2000) [i for i in a if 10 < i <20 or 1000 < i < 2000] """ s2 = """ a = range(2000) [i for i in a if 1000 < i < 2000 or 10 < i <20] """ print timeit.timeit(stmt=s1, number=1000) print timeit.timeit(stmt=s2, number=1000)
Ausführungsergebnisse:
0.253124952316 0.202992200851
Zusammenführungszeichenfolgen zusammenführen
Zusammenführungszeichenfolgen schneller zusammenführen als in einer Schleife zum Zusammenführen.
import timeit s1 = """ a = [str(x) for x in range(2000)] s = '' for i in a: s += i """ s2 = """ a = [str(x) for x in range(2000)] s = ''.join(a) """ print timeit.timeit(stmt=s1, number=1000) print timeit.timeit(stmt=s2, number=1000)
Die laufenden Ergebnisse lauten wie folgt:
python test6.py 0.558945894241 0.422435998917
while 1 und while True
In python2.x sind True und False keine reservierten Schlüsselwörter, sondern a Globale Variablen, was bedeutet, dass Sie dies tun können
>>> True = 0 >>> True 0 >>> if not True: ... print '1' ... 1
Also die folgenden zwei Situationen:
import timeit s1 = """ n = 1000000 while 1: n -= 1 if n <= 0: break """ s2 = """ n = 1000000 while True: n -= 1 if n <= 0: break """ print timeit.timeit(stmt=s1, number=100) print timeit.timeit(stmt=s2, number=100)
Das laufende Ergebnis ist wie folgt:
➜ python test6.py 5.18007302284 6.84624099731
Denn jedes Mal, wenn Sie beurteilen, ob es wahr ist, müssen Sie zuerst den Wert von Wahr ermitteln.
In python3.x wird True zu einem Schlüsselwortargument, sodass die beiden oben genannten Situationen gleich sind.
cProfile, cStringIO und cPickle
Mit der C-Version geschriebene Erweiterungen sind schneller als die nativen. cPickle vs. pickle ist wie folgt:
import timeit s1 = """ import cPickle import pickle n = range(10000) cPickle.dumps(n) """ s2 = """ import cPickle import pickle n = range(10000) pickle.dumps(n) """ print timeit.timeit(stmt=s1, number=100) print timeit.timeit(stmt=s2, number=100)
Die laufenden Ergebnisse sind wie folgt:
➜ python test6.py 0.182178974152 1.70917797089
Vernünftiger Einsatz von Generatoren
Der Unterschied
Verwenden von () ist Der für ein Generatorobjekt erforderliche Speicherplatz hat nichts mit der Größe der Liste zu tun, sodass die Effizienz höher ist.
import timeit s1 = """ [i for i in range (100000)] """ s2 = """ (i for i in range(100000)) """ print timeit.timeit(stmt=s1, number=1000) print timeit.timeit(stmt=s2, number=1000)
Ergebnis:
➜ python test6.py 5.44327497482 0.923446893692
Aber für Situationen, in denen eine Schleifendurchquerung erforderlich ist: Die Verwendung von Iteratoren ist wie folgt nicht effizient:
import timeit s1 = """ ls = range(1000000) def yield_func(ls): for i in ls: yield i+1 for x in yield_func(ls): pass """ s2 = """ ls = range(1000000) def not_yield_func(ls): return [i+1 for i in ls] for x in not_yield_func(ls): pass """ print timeit.timeit(stmt=s1, number=10) print timeit.timeit(stmt=s2, number=10)
Das Ergebnis ist wie folgt:
➜ python test6.py 1.03186702728 1.01472687721
Die Verwendung eines Generators ist also ein Kompromiss, eine umfassende Berücksichtigung von Speicher und Geschwindigkeit.
xrange
在python2.x里xrange 是纯C实现的生成器,相对于range来说,它不会一次性计算出所有值在内存中。但它的限制是只能和整型一起工作:你不能使用long或者float。
import 语句的开销
import语句有时候为了限制它们的作用范围或者节省初始化时间,被卸载函数内部,虽然python的解释器不会重复import同一个模块不会出错,但重复导入会影响部分性能。有时候为了实现懒加载(即使用的时候再加载一个开销很大的模块),可以这么做:
email = None def parse_email(): global email if email is None: import email ... # 这样一来email模块仅会被引入一次,在parse_email()被第一次调用的时候。