时间:2022-10-09 18:01:50 | 浏览:353
作者 | 写代码的明哥
来源 | Python编程时光
如何在运行状态查看源代码
查看函数的源代码,我们通常会使用 IDE 来完成。
比如在 PyCharm 中,你可以 Ctrl +鼠标点击进入函数的源代码。
那如果没有 IDE 呢?
当我们想使用一个函数时,如何知道这个函数需要接收哪些参数呢?
当我们在使用函数时出现问题的时候,如何通过阅读源代码来排查问题所在呢?
这时候,我们可以使用 inspect 来代替 IDE 帮助你完成这些事:
# demo.py
import inspect
def add(x, y):
return x + y
print("===================")
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 zero
During 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 zero
The 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(" ".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 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
☞架构的腐化是必然的!
☞腾讯云 AI 在新基建领域下一盘什么大棋
☞张一鸣是如何练就字节跳动的
☞性能超越最新序列推荐模型,华为诺亚方舟提出记忆增强的图神经网络
☞DevOps 在移动应用程序开发中扮演什么角色?
☞稳定币经济:十大稳定币简史
不论你是Python新手零基础入门Python还是对于Python有了一定的经验积累,这里跟大家分享5个Python计时器,希望伙伴们有能用得上的!Python是一门解释器语言,代码想运行,必须通过解释器执行,Python存在多种解释器,分
之前老看Java岗的一个朋友炫耀他的一份Java核心知识点笔记,当时,我内心毫无波澜,只有一点点酸……其实Python开发也有很多知识点,我一直一来就想要一份Python核心知识点笔记来帮助自己查漏补缺,后来想想,既然一直没有找到让我满意的
机器之心报道编辑:力元、蛋酱2020 年 1 月 1 日,Python 官方结束了对 Python 2 的维护,意味着 Python 2 完全退休,进入 Python 3 时代。之后,关于 Python 4 的发布排期也成为了社区的热门议题
一、python了解python 是脚本语言。python 是一种面向对象的解释型计算机程序设计语言。语法简洁清晰,特色之一是强制用空白符作为语句缩进。优点:跨平台、胶水语言、开源免费、丰富的库 。缺点:运行速度慢、代码不能加密、场景、数据
思维导图是整理知识的一种非常有效的手段,这里分享几张 Python 基础学习的思维导图,可以帮助自己巩固下基础,有需要的小伙伴可以加紧时间收藏哦!获取方式:
第一期计算机等考二级程序设计科目的学习,我们从简单易学的Python开始,今天,就让我们揭开它神秘的面纱!我们先看三组数据:第一组呢是TIOBE指数排行,可以看到我们的Python在今年三月份排在了榜首的位置,相比去年的三月份,Python
都说Python什么都能做,本来我是不信的!直到我在CSDN站内看到了一件真事儿:一位博主贴出了自己10分钟用Python搭建小说网站的全过程!全程只用了2步操作,简直太秀了!!……第一步:爬取小说数据库第二步:用Python的热门框架Dj
去年 11 月,退休失败的 Python 之父 Guido van Rossum 在推特上宣布,退休生活太无聊,从而加入了微软开发者部门。微软自然支持优秀人才的发展,所以赋予了 Guido van Rossum “自由选择项目”的权利,Gu
小编贴心提醒,本文阅读时间9分钟1.Hello world安装完Python之后,打开IDLE(Python GUI) ,该程序是Python语言解释器,你写的语句能够立即运行。我们写下一句著名的程序语句:并按回车,你就能看到这句被K&R引
Python是近年来非常火热的一门编程语言,Python 语言最大的特点就是简单,该特点主要体现在以下 2 个方面:1. Python 语言的语法非常简洁明了,即便是非软件专业的初学者,也很容易上手。2. 和其它编程语言相比,实现同一个功能
经过了前一段时间的python学习,那么今天我们来学习一下字典,首先来看一个简单的字典:在python中,字典是一系列键-值对,每个键都与一个值相关联,例如前面的color和age就是键,yelllow和23就是他们分别对应的值对1.访问字
之前已经讲解了Python解释器的下载和安装,现在就讲下解释器的作用和分类,在讲解任何Python代码之前我们得先知道解释器概念和作用,因为解释器扮演的角色是非常重要的。一、解释器的作用****作用:运行文件(运行代码)****Python
在IDLE中点击help中的Python Docs找到“Python Docs”然后就进入Python文档的界面,在左边找到“搜索”按钮,并点击。找到“搜索”按钮在搜索框中搜索所需要的库名称或者函数名称,就能含有该关键词对应的文件。你会在这
郭一璞 发自 凹非寺 量子位 报道 | 公众号 QbitAI做图表,谁不会?打开Excel,自动就可以生成各种各样的图表。但你看这些图表呢?结合真实地理数据,展现美国每个县的失业率。全球自然灾害统计,类型、规模、时间,一目了然。甚至还有可爱
从例子可以看出,s是个字符串类型。Python有个内置函数bytes()可以将字符串str类型转换成bytes类型,b实际上是一串01的组合,但为了在ide环境中让我们相对直观的观察,它被表现成了b’xe4xb8xadxe6x96