话接上文,我又来了分享我学习到的10个开发技巧啦。学习的路上,只有多多交流才能更好更快的解决难题。在这里还是要推荐下我自己建的Python学习Q群:249029188,群里都是学Python的,如果你想学或者正在学习Python ,欢迎你加入,大家都是软件开发党,不定期分享干货(只有Python软件开发相关的),包括我自己整理的一份2021最新的Python进阶资料和零基础教学,欢迎进阶中和对Python感兴趣的小伙伴加入!
六 . 如何快速计算函数运行时间
计算一个函数的运行时间,你可能会这样子做
import time
start = time.time()
# run the function
end = time.time()
print(end-start)
你看看你为了计算函数运行时间,写了几行代码了。
有没有一种方法可以更方便的计算这个运行时间呢?
现在有一个更简单的方法就是利用内置模块叫 timeit
使用它,只用一行代码即可。
import time
import timeit
def run_sleep(second):
print(second)
time.sleep(second)
只用这一行
print(timeit.timeit(lambda :run_sleep(2), number=5))
运行结果如下
2
2
2
2
2
10.020059824
七. 利用自带的缓存机制提高效率
缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。
数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。
所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。
为了实现这个需求,Python 3.2 + 中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。
这个机制实现于 functool 模块中的 lru_cache 装饰器。
@functools.lru_cache(maxsize=None, typed=False)
参数解读:
**maxsize**:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 2 的幂时,性能最佳
**typed**:若为 True,则不同参数类型的调用将分别缓存。
举个例子
from functools import lru_cache
@lru_cache(None)
def add(x, y):
print("calculating: %s + %s" % (x, y))
return x + y
print(add(1, 2))
print(add(1, 2))
print(add(2, 3))
输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果
calculating: 1 + 2
3
3
calculating: 2 + 3
5
下面这个是经典的斐波那契数列,当你指定的 n 较大时,会存在大量的重复计算
def fib(n):
if n < 2:
return n
return fib(n - 2) + fib(n - 1)
第六点介绍的 timeit,现在可以用它来测试一下到底可以提高多少的效率。
不使用 lru_cache 的情况下,运行时间 31 秒
import timeit
def fib(n):
if n < 2:
return n
return 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 timeit
from functools import lru_cache
@lru_cache(None)
def fib(n):
if n < 2:
return n
return fib(n - 2) + fib(n - 1)
print(timeit.timeit(lambda :fib(500), number=1))
output: 0.0004921059880871326
八. 在程序退出前执行代码的技巧
使用 atexit 这个内置模块,可以很方便的注册退出函数。
不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。
示例如下
如果clean()函数有参数,那么你可以不用装饰器,而是直接调用atexit.register(clean_1, 参数1, 参数2, 参数3=‘xxx’)。
可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit 来得优雅,来得方便,并且它很容易扩展。
但是使用 atexit 仍然有一些局限性,比如:
如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。
如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。
如果你手动调用了os._exit(),你注册的函数无法正常执行。
九. 实现类似 defer 的延迟调用
在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例
import "fmt"
func myfunc() {
fmt.Println("B")
}
func main() {
defer myfunc()
fmt.Println("A")
}
输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 的调用写在函数的第一行,这就是延迟调用。
A
B
那么在 Python 中否有这种机制呢?
当然也有,只不过并没有 Golang 这种简便。
在 Python 可以使用 上下文管理器 达到这种效果
import contextlib
def callback():
print('B')
with contextlib.ExitStack() as stack:
stack.callback(callback)
print('A')
输出如下
A
B
十. 如何流式读取数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:
break
yield chunk
上面的代码,功能上已经没有问题了,但是代码看起来代码还是有些臃肿。
借助偏函数 和 iter 函数可以优化一下代码
from functools import partial
def 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
到这里就结束啦,希望大家能在学习Python的道路上更进一步,我们一起加油。
在这里还是要推荐下我自己建的Python学习Q群:249029188,群里都是学Python的,如果你想学或者正在学习Python ,欢迎你加入,大家都是软件开发党,不定期分享干货(只有Python软件开发相关的),包括我自己整理的一份2021最新的Python进阶资料和零基础教学,欢迎进阶中和对Python感兴趣的小伙伴加入!
**以下内容无用,为本篇博客被搜索引擎抓取使用
(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)
python 是干什么的 零基础学 python 要多久 python 为什么叫爬虫
python 爬虫菜鸟教程 python 爬虫万能代码 python 爬虫怎么挣钱
python 基础教程 网络爬虫 python python 爬虫经典例子
python 爬虫
(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)( ̄︶ ̄)( ̄︶ ̄)
以上内容无用,为本篇博客被搜索引擎抓取使用