5年Python功力,总结了10个开发技巧( 三 )


不使用 lru_cache 的情况下,运行时间 31 秒
import timeitdef fib(n):if n < 2:return nreturn fib(n - 2) + fib(n - 1)print(timeit.timeit(lambda :fib(40), number=1))# output: 31.2725698948由于使用了 lru_cache 后,运行速度实在太快了,所以我将 n 值由 30 调到 500,可即使是这样,运行时间也才 0.0004 秒 。提高速度非常显著 。
import timeitfrom functools import lru_cache@lru_cache(None)def fib(n):if n < 2:return nreturn fib(n - 2) + fib(n - 1)print(timeit.timeit(lambda :fib(500), number=1))# output: 0.0004921059880871326

5年Python功力,总结了10个开发技巧

文章插图
 
在程序退出前执行代码的技巧使用 atexit 这个内置模块,可以很方便的注册退出函数 。
不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数 。
示例如下
5年Python功力,总结了10个开发技巧

文章插图
如果clean函数有参数,那么你可以不用装饰器,而是直接调用atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')
可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit 来得优雅,来得方便,并且它很容易扩展 。
但是使用 atexit 仍然有一些局限性,比如:
  • 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行 。
  • 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行 。
  • 如果你手动调用了os._exit,你注册的函数无法正常执行 。

5年Python功力,总结了10个开发技巧

文章插图
 
实现类似 defer 的延迟调用在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例
import "fmt"func myfunc {fmt.Println("B")}func main {defer myfuncfmt.Println("A")}输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 的调用写在函数的第一行,这就是延迟调用 。
AB那么在 Python 中否有这种机制呢?
当然也有,只不过并没有 Golang 这种简便 。
在 Python 可以使用 上下文管理器达到这种效果
import contextlibdef callback:print('B')with contextlib.ExitStack as stack:stack.callback(callback)print('A')输出如下
AB
5年Python功力,总结了10个开发技巧

文章插图
 
如何流式读取数G超大文件使用 with...open... 可以从一个文件中读取数据,这是所有 Python 开发者都非常熟悉的操作 。
但是如果你使用不当,也会带来很大的麻烦 。
比如当你使用了 read 函数,其实 Python 会将文件的内容一次性的全部载入内存中,如果文件有 10 个G甚至更多,那么你的电脑就要消耗的内存非常巨大 。
# 一次性读取with open("big_file.txt", "r") as fp:content = fp.read对于这个问题,你也许会想到使用 readline 去做一个生成器来逐行返回 。
def read_from_file(filename):with open(filename, "r") as fp:yield fp.readline可如果这个文件内容就一行呢,一行就 10个G,其实你还是会一次性读取全部内容 。
最优雅的解决方法是,在使用 read 方法时,指定每次只读取固定大小的内容,比如下面的代码中,每次只读取 8kb 返回 。
def read_from_file(filename, block_size = 1024 * 8):with open(filename, "r") as fp:while True:chunk = fp.read(block_size)if not chunk:breakyield chunk上面的代码,功能上已经没有问题了,但是代码看起来代码还是有些臃肿 。
借助偏函数 和 iter 函数可以优化一下代码
from functools import partialdef read_from_file(filename, block_size = 1024 * 8):with open(filename, "r") as fp:for chunk in iter(partial(fp.read, block_size), ""):yield chunk明哥原创文都已传至 Github:
https://github.com/iswbm/PythonCodingTime




推荐阅读