如果不首先想想这句Knuth的名言,就开始进行优化工作是不明智的。可是,你很快写出来加入一些特性的代码,可能会很丑陋,你需要注意了。这篇文章就是为这时候准备的。
那么接下来就是一些很有用的工具和模式来快速优化Python。它的主要目的很简单:尽快发现瓶颈,修复它们并且确认你修复了它们。
写一个测试
在你开始优化前,写一个高级测试来证明原来代码很慢。你可能需要采用一些最小值数据集来复现它足够慢。通常一两个显示运行时秒的程序就足够处理一些改进的地方了。
有一些基础测试来保证你的优化没有改变原有代码的行为也是很必要的。你也能够在很多次运行测试来优化代码的时候稍微修改这些测试的基准。
那么现在,我们来来看看优化工具把。
简单的计时器
计时器很简单,这是一个最灵活的记录执行时间的方法。你可以把它放到任何地方并且副作用很小。运行你自己的计时器非常简单,并且你可以将其定制,使它以你期望的方式工作。例如,你个简单的计时器如下:
import time def timefunc(f): def f_timer(*args, **kwargs): start = time.time() result = f(*args, **kwargs) end = time.time() print f.__name__, 'took', end - start, 'time' return result return f_timer def get_number(): for x in xrange(5000000): yield x @timefunc def expensive_function(): for x in get_number(): i = x ^ x ^ x return 'some result!' # prints "expensive_function took 0.72583088875 seconds" result = expensive_function()
当然,你可以用上下文管理来让它功能更加强大,添加一些检查点或者一些其他的功能:
import time class timewith(): def __init__(self, name=''): self.name = name self.start = time.time() @property def elapsed(self): return time.time() - self.start def checkpoint(self, name=''): print '{timer} {checkpoint} took {elapsed} seconds'.format( timer=self.name, checkpoint=name, elapsed=self.elapsed, ).strip() def __enter__(self): return self def __exit__(self, type, value, traceback): self.checkpoint('finished') pass def get_number(): for x in xrange(5000000): yield x def expensive_function(): for x in get_number(): i = x ^ x ^ x return 'some result!' # prints something like: # fancy thing done with something took 0.582462072372 seconds # fancy thing done with something else took 1.75355315208 seconds # fancy thing finished took 1.7535982132 seconds with timewith('fancy thing') as timer: expensive_function() timer.checkpoint('done with something') expensive_function() expensive_function() timer.checkpoint('done with something else') # or directly timer = timewith('fancy thing') expensive_function() timer.checkpoint('done with something')
计时器还需要你做一些挖掘。包装一些更高级的函数,并且确定瓶颈在哪,然后深入的函数里,能够不停的重现。当你发现一些不合适的代码,修复它,然后测试一遍以确认它被修复了。
一些小技巧:不要忘了好用的timeit模块!它对小块代码做基准测试而不是实际调查更加有用。
- Timer 优点:很容易理解和实现。也非常容易在修改后进行比较。对于很多语言都适用。
- Timer 缺点:有时候对于非常复杂的代码有点过于简单,你可能会花更多时间放置或移动引用代码而不是修复问题!
内建优化器
启用内建的优化器就像是用一门大炮。它非常强大,但是有点不太好用,使用和解释起来比较复杂。
你可以了解更多关于profile模块的东西,但是它的基础是非常简单的:你能够启用和禁用优化器,而且它能打印所有的函数调用和执行时间。它能给你编译和打印出输出。一个简单的装饰器如下:
import cProfile def do_cprofile(func): def profiled_func(*args, **kwargs): profile = cProfile.Profile() try: profile.enable() result = func(*args, **kwargs) profile.disable() return result finally: profile.print_stats() return profiled_func def get_number(): for x in xrange(5000000): yield x @do_cprofile def expensive_function(): for x in get_number(): i = x ^ x ^ x return 'some result!' # perform profiling result = expensive_function()
在上面代码的情况下,你应该看到有些东西在终端打印出来,打印的内容如下:
5000003 function calls in 1.626 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 5000001 0.571 0.000 0.571 0.000 timers.py:92(get_number) 1 1.055 1.055 1.626 1.626 timers.py:96(expensive_function) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
你可以看到,它给出了不同函数的调用次数,但它遗漏了一些关键的信息:是哪个函数让运行这么慢?
可是,这对于基础优化来说是个好的开始。有时候甚至能用更少的精力找到解决方案。我经常用它来在深入挖掘究竟是哪个函数慢或者调用次数过多之前来调试程序。
- 内建优点:没有额外的依赖并且非常快。对于快速的高等级检查非常有用。
- 内建缺点:信息相对有限,需要进一步的调试;报告有点不太直接,尤其是对于复杂的代码。
Line Profiler
如果内建的优化器是一门大炮,那么line profiler可以看作是一门离子加农炮。它非常的重量级和强大。
在这个例子里,我们会用非常棒的line_profiler库。为了容易使用,我们会再次用装饰器包装一下,这种简单的方法也可以防止把它放在生产代码里。
try: from line_profiler import LineProfiler def do_profile(follow=[]): def inner(func): def profiled_func(*args, **kwargs): try: profiler = LineProfiler() profiler.add_function(func) for f in follow: profiler.add_function(f) profiler.enable_by_count() return func(*args, **kwargs) finally: profiler.print_stats() return profiled_func return inner except ImportError: def do_profile(follow=[]): "Helpful if you accidentally leave in production!" def inner(func): def nothing(*args, **kwargs): return func(*args, **kwargs) return nothing return inner def get_number(): for x in xrange(5000000): yield x @do_profile(follow=[get_number]) def expensive_function(): for x in get_number(): i = x ^ x ^ x return 'some result!' result = expensive_function()
如果你运行上面的代码,你就可以看到一下的报告:
Timer unit: 1e-06 s File: test.py Function: get_number at line 43 Total time: 4.44195 s Line # Hits Time Per Hit % Time Line Contents ============================================================== 43 def get_number(): 44 5000001 2223313 0.4 50.1 for x in xrange(5000000): 45 5000000 2218638 0.4 49.9 yield x File: test.py Function: expensive_function at line 47 Total time: 16.828 s Line # Hits Time Per Hit % Time Line Contents ============================================================== 47 def expensive_function(): 48 5000001 14090530 2.8 83.7 for x in get_number(): 49 5000000 2737480 0.5 16.3 i = x ^ x ^ x 50 1 0 0.0 0.0 return 'some result!'
你可以看到,有一个非常详细的报告,能让你完全洞悉代码运行的情况。不想内建的cProfiler,它能计算话在语言核心特性的时间,比如循环和导入并且给出在不同的行花费的时间。
这些细节能让我们更容易理解函数内部。如果你在研究某个第三方库,你可以直接将其导入并加上装饰器来分析它。
一些小技巧:只装饰你的测试函数并将问题函数作为接下来的参数。
- Line Profiler 优点:有非常直接和详细的报告。能够追踪第三方库里的函数。
- Line Profiler 缺点:因为它会让代码比真正运行时慢很多,所以不要用它来做基准测试。这是额外的需求。
总结和最佳实践
你应该用更简单的工具来对测试用例进行根本的检查,并且用更慢但能显示更多细节的line_profiler来深入到函数内部。
九成情况下,你可能会发现在一个函数里循环调用或一个错误的数据结构消耗了90%的时间。一些调整工具是非常适合你的。
如果你仍然觉得这太慢,而是用一些你自己的秘密武器,如比较属性访问技术或调整平衡检查技术。你也可以用如下的方法:
1.忍受缓慢或者缓存它们
2.重新思考整个实现
3.更多使用优化的数据结构
4.写一个C扩展
注意了,优化代码是种罪恶的快感!用合适的方法来为你的Python代码加速很有意思,但是注意不要破坏了本身的逻辑。可读的代码比运行速度更重要。先把它缓存起来再进行优化其实更好。

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。Python以简洁和强大的生态系统著称,C 则以高性能和底层控制能力闻名。

2小時內可以學會Python的基本編程概念和技能。 1.學習變量和數據類型,2.掌握控制流(條件語句和循環),3.理解函數的定義和使用,4.通過簡單示例和代碼片段快速上手Python編程。

Python在web開發、數據科學、機器學習、自動化和腳本編寫等領域有廣泛應用。 1)在web開發中,Django和Flask框架簡化了開發過程。 2)數據科學和機器學習領域,NumPy、Pandas、Scikit-learn和TensorFlow庫提供了強大支持。 3)自動化和腳本編寫方面,Python適用於自動化測試和系統管理等任務。

兩小時內可以學到Python的基礎知識。 1.學習變量和數據類型,2.掌握控制結構如if語句和循環,3.了解函數的定義和使用。這些將幫助你開始編寫簡單的Python程序。

如何在10小時內教計算機小白編程基礎?如果你只有10個小時來教計算機小白一些編程知識,你會選擇教些什麼�...

使用FiddlerEverywhere進行中間人讀取時如何避免被檢測到當你使用FiddlerEverywhere...

Python3.6環境下加載Pickle文件報錯:ModuleNotFoundError:Nomodulenamed...

如何解決jieba分詞在景區評論分析中的問題?當我們在進行景區評論分析時,往往會使用jieba分詞工具來處理文�...


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。