当前位置: 首页 > 科技观察

5年Python功底,总结出10大开发技巧

时间:2023-03-11 22:17:16 科技观察

1.如何查看运行状态下的源码?要查看函数的源代码,我们通常使用IDE来完成。比如在PyCharm中,可以Ctrl+鼠标点击进入函数的源代码。如果没有IDE怎么办?当我们要使用一个函数的时候,我们怎么知道这个函数需要接收哪些参数呢?当我们在使用一个函数的时候遇到了问题,如何通过阅读源码来排查问题呢?这时候我们就可以用inspect代替IDE来帮你完成这些事情了。#demo.pyimportinspectdefadd(x,y):returnx+yprint("====================")print(inspect.getsource(add))结果为如下$pythondemo.py=====================defadd(x,y):returnx+y2。如何关闭异常的自动上下文?当你在处理一个异常的时候,由于处理不当或者其他问题,再次抛出另一个异常时,抛出的异常也会携带原来的异常信息。像这样。try:print(1/0)exceptExceptionasexc:raiseRuntimeError("Somethingbadhappened")从输出中可以看到两条异常信息Traceback(mostrecentcallast):File"demo.py",line2,inprint(1/0)ZeroDivisionError:divisionbyzeroDuringhandlingoftheaboveexception,anotherexceptionoccurred:Traceback(mostrecentcalllast):File"demo.py",line4,inraiseRuntimeError("Somethingbadhappened")RuntimeError:Somethingbadhappened如果在exceptionhandler或finally块中引发异常,默认情况下,异常机制将隐式工作将附加先前的异常作为新异常的__context__属性。这是Python默认启用的自相关异常上下文。如果你想自己控制这个上下文,可以加上一个from关键字(from语法有一个限制,即第二个表达式必须是另一个异常类或实例。)来表明你的新异常直接是由哪个异常引起的造成的。try:print(1/0)exceptExceptionasexc:raiseRuntimeError("Somethingbadhappened")fromexc输出以下Traceback(mostrecentcalllast):File"demo.py",line2,inprint(1/0)ZeroDivisionError:divisionbyzeroTheaboveexceptionwasthedirectcauseofthefollowingmostcallastexception:Traceback():File"demo.py",line4,inraiseRuntimeError("Somethingbadhappened")fromexcRuntimeError:Somethingbadhappened当然也可以通过with_traceback方法为异常设置上下文__context__属性,也可以更好的展示在回溯异常信息。try:print(1/0)exceptExceptionasexc:raiseRuntimeError("badthing").with_traceback(exc)最后,如果我想完全关闭这种自动关联异常上下文的机制呢?我们还能做什么?你可以使用raise...fromNone。从下面的例子来看,没有原来的异常。$catdemo.pytry:print(1/0)exceptExceptionasexc:raiseRuntimeError("Somethingbadhappened")fromNone$$pythondemo.pyTraceback(mostrecentcallast):文件"demo.py",line4,inraiseRuntimeError("Somethingbadhappened")fromNoneRuntimeError:Somethingbadhappened(PythonCodingTime)3.查看包搜索路径最快的方法当你导入一个包或者模块时,Python会去目录下的一些Search,而且这些目录是优先的,正常人会使用sys.path来查看。>>>importsys>>>frompprintimportpprint>>>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']>>>有没有更快的方法?有没有办法我什至不需要进入控制台模式?你可能会想到这个,但是这个和上面本质上是一样的[wangbm@localhost~]$python-c"p??rint('\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-msitesys。路径=['/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从输出你可以find,这一栏的路径会比sys.path更完整,里面包含了用户环境的目录4.将嵌套for循环写成一行我们经常使用下面的嵌套for循环代码list1=range(1,3)list2=range(4,6)list3=range(7,9)foritem1inlist1:foritem2inlist2:foritem3inlist3:print(item1+item2+item3)这里只是三个for循环,实际编码时可能层数更多。这种代码可读性很差。很多人不想这样写,但是也没有更好的写法。这里介绍一种我经常使用的写法,使用itertools库实现更优雅可读的代码。fromitertoolsimportproductlist1=range(1,3)list2=range(4,6)list3=range(7,9)foritem1,item2,item3inproduct(list1,list2,list3):print(item1+item2+item3)输出如下$pythondemo.py12131314131414155。如何使用print输出日志初学者喜欢使用print来调试代码,记录程序的运行过程。但是print只会将内容输出到终端,并不能持久化到日志文件,不利于排错。如果你热衷于使用print来调试代码(虽然这不是最佳实践),记录下程序的运行过程,那么下面介绍的print用法可能对你有用。作为一个函数,Python3中的print变得更加强大,因为它可以接收更多的参数。指定一些参数可以将打印的内容输出到日志文件中。代码如下:>>>withopen('test.log',mode='w')asf:...print('hello,p??ython',file=f,flush=True)>>>exit$cattest.loghello,python6。如何快速计算一个函数的运行时间计算一个函数的运行时间Time,可以这样importtimestart=time.time#runthefunctionend=time.timeprint(end-start)。看看你写了多少行代码来计算函数的运行时间。有没有办法更方便地计算这个运行时间?有。有一个名为timeit的内置模块可以使用它,只需一行代码importtimeimporttimeitdefrun_sleep(second):print(second)time.sleep(second)#Onlyusethislineprint(timeit.timeit(lambda:run_sleep(2),number=5))运行结果如下2222210.0200598247。使用内置的缓存机制来提高效率。缓存是一种保存定量数据以满足后续采集需求的处理方法,旨在加快数据采集速度。数据生成过程可能需要进行计算、正则化、远程获取等操作。如果同一个数据需要多次使用,每次都重新生成会浪费很多时间。因此,如果将计算或远程请求等操作得到的数据缓存起来,会加速后续的数据获取需求。为了实现这个需求,Python3.2+为我们提供了一种机制,不需要你写这样的逻辑代码就可以轻松实现。该机制在functool模块中的lru_cache装饰器中实现。@functools.lru_cache(maxsize=None,typed=False)参数解释:maxsize:这个函数的调用结果最多可以缓存多少个,如果是None则没有限制,当设置为的幂时2、性能最好typed:如果为True,不同参数类型的调用会被单独缓存。例如fromfunctoolsimportlru_cache@lru_cache(None)defadd(x,y):print("calculating:%s+%s"%(x,y))returnx+yprint(add(1,2))print(add(1,2))print(add(2,3))输出如下,可以看到第二次调用并没有真正执行函数体,而是直接返回缓存中的结果计算:1+233计算:2+35下面就是在经典的斐波那契数列中,当你指定一个很大的n时,会有很多重复的计算defib(n):ifn<2:returnnreturnfib(n-2)+fib(n-1)第六点介绍timeit,现在你可以用它来测试效率能提高多少。不使用lru_cache,运行时间为31秒#output:31.2725698948由于使用lru_cache后运行速度太快,我将n的值从30调整为500,但即便如此,运行时间也只有0.0004秒。速度的提升非常显着。importtimeitfromfunctoolsimportlru_cache@lru_cache(None)defib(n):ifn<2:returnreturnfib(n-2)+fib(n-1)print(timeit.timeit(lambda:fib(500),number=1))#output:0.00049210598808713268。程序退出前执行代码的技巧使用内置模块atexit,注册退出函数非常方便。无论你在哪里导致程序崩溃,你注册的函数都会被执行。例子如下如果clean函数有参数,那么可以不带装饰器直接调用atexit.register(clean_1,parameter1,parameter2,parameter3='xxx')。可能还有其他方法可以满足此需求,但它肯定比不使用atexit更优雅、更方便并且易于扩展。但是使用atexit还是有一些局限性的,例如:如果程序被你没有处理过的系统信号杀死,那么注册的函数就不能正常执行。如果发生严重的Python内部错误,您注册的函数将无法正常执行。如果你手动调用os._exit,你注册的函数将无法正常执行。9.在Golang中实现一个类似defer的延迟调用机制。关键字是延迟。例如下面的例子import"fmt"funcmyfunc{fmt.Println("B")}funcmain{defermyfuncfmt.Println("A")}输出结果如下,myfunc的调用会在函数前一步完成returns,即使你把myfunc的调用写在函数的第一行,这也是延迟调用。AB那么Python中有这样的机制吗?当然有,但不是Golang那么简单。在Python中,可以使用上下文管理器来实现这种效果。importcontextlibdefcallback:print('B')withcontextlib.ExitStackasstack:stack.callback(callback)print('A')输出以下AB10。...打开...从文件中读取数据,这是所有Python开发人员都熟悉的操作。但是如果使用不当,也会造成很多麻烦。例如,当你使用read函数时,Python实际上会一次将文件的所有内容加载到内存中。如果文件有10G或更多,那么您的计算机将消耗大量内存。#One-timereadwithopen("big_file.txt","r")asfp:content=fp.read对于这个问题,你可能会想到用readline作为生成器逐行返回。defread_from_file(filename):withopen(filename,"r")asfp:yieldfp.readline但是如果这个文件的内容只有一行,一行10G,其实你还是会一下子读完所有的内容。最优雅的方案是在使用read方法时指定每次只读取固定大小的内容。比如下面的代码,每次只读取并返回8kb。defread_from_file(filename,block_size=1024*8):withopen(filename,"r")asfp:whileTrue:chunk=fp.read(block_size)ifnotchunk:breakyieldchunk上面的代码在功能上没有问题,但是代码看起来还是代码有点臃肿。借助partial函数和iter函数,可以优化代码fromfunctoolsimportpartialdefread_from_file(filename,block_size=1024*8):withopen(filename,"r")asfp:forchunkiniter(partial(fp.read,block_size),""):yieldchunk