作者 | 写代码的明哥

来源 |Python编程时光(ID: Cool-Python)

如何在运行状态查看源代码?

查看函数的源代码,我们通常会使用 IDE 来完成。

比如在 PyCharm 中,你可以 Ctrl + 鼠标点击 进入函数的源代码。

那如果没有 IDE 呢?

当我们想使用一个函数时,如何知道这个函数需要接收哪些参数呢?

当我们在使用函数时出现问题的时候,如何通过阅读源代码来排查问题所在呢?

这时候,我们可以使用 inspect 来代替 IDE 帮助你完成这些事。

# demo.py
import inspectdef add(x, y):return x + yprint("===================")
print(inspect.getsource(add))

运行结果如下

$ python demo.py
===================
def add(x, y):return x + y


如何关闭异常自动关联上下文?

当你在处理异常时,由于处理不当或者其他问题,再次抛出另一个异常时,往外抛出的异常也会携带原始的异常信息。

就像这样子。

try:print(1 / 0)
except Exception as exc:raise RuntimeError("Something bad happened")

从输出可以看到两个异常信息

Traceback (most recent call last):File "demo.py", line 2, in <module>print(1 / 0)
ZeroDivisionError: division by zeroDuring handling of the above exception, another exception occurred:Traceback (most recent call last):File "demo.py", line 4, in <module>raise RuntimeError("Something bad happened")
RuntimeError: Something bad happened

如果在异常处理程序或 finally 块中引发异常,默认情况下,异常机制会隐式工作会将先前的异常附加为新异常的 __context__属性。这就是 Python 默认开启的自动关联异常上下文。

如果你想自己控制这个上下文,可以加个 from 关键字(from 语法会有个限制,就是第二个表达式必须是另一个异常类或实例。),来表明你的新异常是直接由哪个异常引起的。

try:print(1 / 0)
except Exception as exc:raise RuntimeError("Something bad happened") from exc

输出如下

Traceback (most recent call last):File "demo.py", line 2, in <module>print(1 / 0)
ZeroDivisionError: division by zeroThe above exception was the direct cause of the following exception:Traceback (most recent call last):File "demo.py", line 4, in <module>raise RuntimeError("Something bad happened") from exc
RuntimeError: Something bad happened

当然,你也可以通过with_traceback()方法为异常设置上下文__context__属性,这也能在traceback更好的显示异常信息。

try:print(1 / 0)
except Exception as exc:raise RuntimeError("bad thing").with_traceback(exc)

最后,如果我想彻底关闭这个自动关联异常上下文的机制?有什么办法呢?

可以使用 raise...from None,从下面的例子上看,已经没有了原始异常

$ cat demo.py
try:print(1 / 0)
except Exception as exc:raise RuntimeError("Something bad happened") from None
$
$ python demo.py
Traceback (most recent call last):File "demo.py", line 4, in <module>raise RuntimeError("Something bad happened") from None
RuntimeError: Something bad happened
(PythonCodingTime)

最快查看包搜索路径的方式

当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 查看。

>>> import sys
>>> from pprint import pprint
>>> pprint(sys.path)
['','/usr/local/Python3.7/lib/python37.zip','/usr/local/Python3.7/lib/python3.7','/usr/local/Python3.7/lib/python3.7/lib-dynload','/home/wangbm/.local/lib/python3.7/site-packages','/usr/local/Python3.7/lib/python3.7/site-packages']
>>> 

那有没有更快的方式呢?

我这有一种连 console 模式都不用进入的方法呢?

你可能会想到这种,但这本质上与上面并无区别

[wangbm@localhost ~]$ python -c "print('\n'.join(__import__('sys').path))"/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg
/usr/lib/python2.7/site-packages/redis-3.0.1-py2.7.egg
/usr/lib64/python27.zip
/usr/lib64/python2.7
/usr/lib64/python2.7/plat-linux2
/usr/lib64/python2.7/lib-tk
/usr/lib64/python2.7/lib-old
/usr/lib64/python2.7/lib-dynload
/home/wangbm/.local/lib/python2.7/site-packages
/usr/lib64/python2.7/site-packages
/usr/lib64/python2.7/site-packages/gtk-2.0
/usr/lib/python2.7/site-packages

这里我要介绍的是比上面两种都方便的多的方法,一行命令即可解决

[wangbm@localhost ~]$ python3 -m site
sys.path = ['/home/wangbm','/usr/local/Python3.7/lib/python37.zip','/usr/local/Python3.7/lib/python3.7','/usr/local/Python3.7/lib/python3.7/lib-dynload','/home/wangbm/.local/lib/python3.7/site-packages','/usr/local/Python3.7/lib/python3.7/site-packages',
]
USER_BASE: '/home/wangbm/.local' (exists)
USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists)
ENABLE_USER_SITE: True

从输出你可以发现,这个列的路径会比 sys.path 更全,它包含了用户环境的目录。

将嵌套 for 循环写成单行

我们经常会如下这种嵌套的 for 循环代码

list1 = range(1,3)
list2 = range(4,6)
list3 = range(7,9)
for item1 in list1:for item2 in list2:for item3 in list3:print(item1+item2+item3)

这里仅仅是三个 for 循环,在实际编码中,有可能会有更层。

这样的代码,可读性非常的差,很多人不想这么写,可又没有更好的写法。

这里介绍一种我常用的写法,使用 itertools 这个库来实现更优雅易读的代码。

from itertools import product
list1 = range(1,3)
list2 = range(4,6)
list3 = range(7,9)
for item1,item2,item3 in product(list1, list2, list3):print(item1+item2+item3)

输出如下

$ python demo.py
12
13
13
14
13
14
14
15

如何使用 print 输出日志

初学者喜欢使用 print 来调试代码,并记录程序运行过程。

但是 print 只会将内容输出到终端上,不能持久化到日志文件中,并不利于问题的排查。

如果你热衷于使用 print 来调试代码(虽然这并不是最佳做法),记录程序运行过程,那么下面介绍的这个 print 用法,可能会对你有用。

Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大,指定一些参数可以将 print 的内容输出到日志文件中

代码如下:

>>> with open('test.log', mode='w') as f:
...     print('hello, python', file=f, flush=True)
>>> exit()$ cat test.log
hello, python


如何快速计算函数运行时间

计算一个函数的运行时间,你可能会这样子做

import timestart = time.time()# run the functionend = time.time()
print(end-start)

你看看你为了计算函数运行时间,写了几行代码了。

有没有一种方法可以更方便的计算这个运行时间呢?

有。

有一个内置模块叫 timeit

使用它,只用一行代码即可

import time
import timeitdef 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 + yprint(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 nreturn fib(n - 2) + fib(n - 1)

第六点介绍的 timeit,现在可以用它来测试一下到底可以提高多少的效率。

不使用 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 timeit
from 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

在程序退出前执行代码的技巧

使用 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 contextlibdef 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: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

博客链接:

http://python.iswbm.com/en/latest/c03/c03_07.html

推荐阅读
  • 全网唯一秃头数据集:20万张人像,网罗各类秃头

  • 阿里云科学家入选计算机顶会HPCA名人堂,他是什么来头?

  • 干货 | 基于SRS直播平台的监控系统之实现思路与过程

  • 程序员在这些地方敲代码,普通笔记本根本扛不住

  • 又一国产数据库诞生!腾讯发布 TGDB,实时查询比 Neo4j 快 20-150 倍

  • 力挺比特币的世界第2交易员:仅次于索罗斯,连续25年无亏损

你点的每个“在看”,我都认真当成了AI

5年Python功力,总结了10个开发技巧相关推荐

  1. python大神-6年Python大神总结10个开发技巧,80%的人都不会

    值得一提的是,这 10 个技巧全部收录在我自己写的 <Python黑魔法指南>里 另外要特别注意:光理论是不够的.这里顺便免费送大家一套2020最新python入门到高级项目实战视频教程, ...

  2. python大神的程序_6年Python大神总结10个开发技巧,80%的人都不会

    值得一提的是,这 10 个技巧全部收录在我自己写的 <Python黑魔法指南>里 另外要特别注意:光理论是不够的.这里顺便免费送大家一套2020最新python入门到高级项目实战视频教程, ...

  3. python音频分析工具_『开发技巧』Python音频操作工具PyAudio上手教程

    『开发技巧』Python音频操作工具PyAudio上手教程 ​ 0.引子 当需要使用Python处理音频数据时,使用python读取与播放声音必不可少,下面介绍一个好用的处理音频PyAudio工具包. ...

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

    今天给大家分享 10 个我平时整理非常实用的 Python 开发小技巧.一起来看看吧. 1. 如何在运行状态查看源代码? 查看函数的源代码,我们通常会使用 IDE 来完成. 比如在 PyCharm 中 ...

  5. 基础必备 | Python处理文件系统的10种方法

    作者 | Jeff Hale 译者 | 风车云马:责编 | Jane,Rachel 出品 | Python大本营(ID:pythonnews) [导读]在编写一些Python程序的时候,我们常常需要与 ...

  6. 想轻松入门Python编程,这10个经典案例你还不知道嘛?

    一直以来,Python都是一门很简单的编程语言,其实无论你有没有基础,学起来都不难. 但,必须有方法,而最好的方法其实就是学+练,即:基本常识+这10经典案例. 而同时有着系统的Python基础知识点 ...

  7. Python 机器学习库 Top 10,你值得拥有!

    随着人工智能技术的发展与普及,Python 超越了许多其他编程语言,成为了机器学习领域中最热门最常用的编程语言之一.有许多原因致使 Python 在众多开发者中如此受追捧,其中之一便是其拥有大量的与机 ...

  8. python小程序源代码-10个Python练手小程序

    [程序1] 题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 1.程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去 掉不满足条件的排列. ...

  9. Python 网络爬虫笔记10 -- Scrapy 使用入门

    Python 网络爬虫笔记10 – Scrapy 使用入门 Python 网络爬虫系列笔记是笔者在学习嵩天老师的<Python网络爬虫与信息提取>课程及笔者实践网络爬虫的笔记. 课程链接: ...

最新文章

  1. sqlserver可将字符转成数字再进行sum,如果varchar类型中存放的都是数字
  2. python数据类型-Python核心数据类型概览
  3. 直播 | WWW 2021论文解读:强化学习与互信息驱动的子图神经网络
  4. 对自己有用的VS调试技巧
  5. 【Python笔记】异常处理
  6. LDAP命令介绍---dstune
  7. Sublime Text 2安装汉化破解、插件包安装教程
  8. java源码编译指令
  9. android 静默暗转_Android 7.0 静默安装
  10. Mac 在当前目录打开终端
  11. python 分词 词性_pyhanlp——分词与词性标注
  12. 工控变成用c语言还是plc,浅析工控机与PLC的区别
  13. 如何以最快速度将Vue接入在线客服系统?
  14. [PHP] 新浪企业邮箱登录功能难点梳理
  15. global 与 $GLOBALS用法
  16. java 室内3d_室内地图制作-首款实时室内绘制室内地图-3D室内地图
  17. java exchange 日历_如何通过EWS-API 获取所有会议室的日历信息
  18. python制作qq登录界面_用Python实现一个最新QQ办公版(TIM)的登录界面
  19. 【ArcGIS错误异常100问】之002:Error 000735 简化容差:值是必需的(简化线、简化面工具)
  20. Matlab基本初等函数大全

热门文章

  1. NLP(Natural Language Processing)
  2. 性能测试分析之带宽瓶颈的疑惑
  3. Android -- TextView与EditText 同步显示
  4. TSM简介(一)- 原理与特点
  5. Java中如何合并有个具有相同key的Map
  6. java验证码源码_Java通用验证码程序及应用示例(提供源码下载)
  7. python 抛出异常raise
  8. Python中的变量以及赋值语句
  9. Swift学习:自动引用计数
  10. 『TensorFlow』命令行参数解析