5年 Python 功力,总结了 10个开发技巧!高效率开发真正的秘诀(二)

话接上文,我又来了分享我学习到的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 爬虫
(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)(* ̄︶ ̄)( ̄︶ ̄)( ̄︶ ̄)
以上内容无用,为本篇博客被搜索引擎抓取使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值