当前位置: 首页 > 后端技术 > Python

Python测试:小心简洁背后隐藏的陷阱

时间:2023-03-26 11:25:05 Python

很多朋友会疑惑为什么我写的代码这么慢?记得我读研究生的时候,我的导师来台湾,给我讲了他的亲身经历。他说,他写的算法,半小时之内,电脑就出结果了,而且是在个人电脑上。台湾学生2-3天不能算。问了之后说还在算。导师问的问题是关于裂纹的应力分析。简单点说,比如子弹打在玻璃上,玻璃会不会出现裂纹,裂纹周围的应力是如何分布的,出现裂纹后玻璃能否继续达到使用的应力承受能力?这是一个非常复杂的计算,可以说导师的算法非常好。当然,我们做的是计算机数值计算,对性能的追求可以说是达到了极致。在应用领域,开发成本也是一个非常重要的考虑因素。我公司的流体动力学仿真软件一直非常追求性能。同时,作为商业软件开发公司,我们也非常关心开发成本。从计算性能到可视化性能的各个方面都需要高度的分析时间和经验。今天我们来谈谈性能。关于性能和开发1.编程之美的标准是什么?美丑是判断,既然是判断,自然有标准问题,也就是标准测试。作为算法工程师,自然是先完成功能,再完成性能。但是,功能的事情通常容易描述,标准也比较明确,但是性能的事情就没完没了。一般情况下,我们的判断标准是在功能满足客户需求的情况下,以市场上当前软件水平的客户体验效果为标准。别人一个操作等0.1秒,我们0.1秒内就可以完成。2、牺牲业绩的背后,是组织形式的自由和更广阔的人才空间。这句话有点冠冕堂皇。简而言之就是三个方面:(1)我们可以更方便的组织开发工作,让架构工程师和算法工程师有更多的工具和选择。(2)在较低的性能要求下,对开发人员的水平要求会大大降低,可以大大降低人员成本。(3)需要更短的开发周期,人容易找,算法组织好,开发效率更高。Python性能测试1.逻辑判断快或者基本操作快get_time()foriinrange(100000):c=a>bt2=get_time()foriinrange(100000):ifa>b:pass3=get_time()print("addtime:",t1-t0)print(">time:",t2-t1)print("iftime:",t3-t2)运行结果:addtime:0.005983114242553711">"time:0.008975982666015625iftime:0.00628209114074707可以看出判断语句和基本运行速度都是一个数量级,如果1个周期,耗时是1e-8秒数量级,也就是1e-5毫秒。第二个循环中额外的3e-8秒是由于分配造成的。2.调用python内置的max和if语句判断代码:importtimedefget_time():returntime.time()defmax1(a,b):ifa>b:returnaelse:returnba=1b=2c=0t0=get_time()foriinrange(100000):ifa>b:c=aelse:c=bt1=get_time()foriinrange(100000):c=max(a,b)t2=get_time()foriinrange(100000):c=max1(a,b)t3=get_time()print("checktime:",t1-t0,"rate:","1")print("maxtime:",t2-t1,"rate",(t2-t1)/(t1-t0))print("max1时间:",t3-t2,"rate",(t3-t2)/(t1-t0))这里我们判断两个值比较大。用了python自带的max函数,if判断,和if判断放进一个函数max1。我们看一下结果:checktime:0.009987831115722656rate:1maxtime:0.019948720932006836rate1.9973025876062256max1time:0.014958620071411133rate1.497684522104459最快的,是直接if判断。我们用它作为比较的标准。内置最大速度为2倍,max1为1.5倍。所以,自己写的函数在性能上不一定比内置函数慢。我说的是这里max不用慢,因为max还有其他的判断处理。所以,最好只用你用的,恰到好处。但是在这里,为什么要这样比较呢?显然max1的速度比直接判断慢,这个不一定。我们稍后再看。3.一个类的例子我今天发了一个微头条,是关于一个判断点是否在矩形区域内的类。也就是这个标题让我想试试看谁的性能更快。现在让我们检查一下!主要是比较,使用距离函数判断和直接使用判断来区分性能上的差异。对于max和abs定义的距离使用if语句,而不是maxall使用if判断代码比较长:首先定义一个Rect类,类Area1(Rect):def__init__(self,x,y,w,h):super().__init__(x,y,w,h)@propertydefcenter(self):returnself.x+self.w/2,self.y+self.h/2defnorm(self,pos=None):"""定义一个距离"""wph=self.w/self.h如果pos不是None:norm0=max(abs(pos[0]-self.center[0]),wph*abs(pos[1]-self.center[1]))returnnorm0else:radius=max(abs(self.x-self.center[0]),wph*abs(self.y-self.center[1]))return在我们定义半径的第一种情况下,我们使用由max和abs定义的距离。类名是Aera1。我们的类Area1(Rect):def__init__(self,x,y,w,h):super().__init__(x,y,w,h)@propertydefcenter(self):returnself.x+self.w/2,self.y+self.h/2defnorm(self,pos=None):"""定义一个距离"""wph=self.w/self.hifposisnotNone:norm0=max(abs(pos[0]-self.center[0]),wph*abs(pos[1]-self.center[1]))返回norm0else:radius=max(abs(self.x-self.center[0]),wph*abs(self.y-self.center[1]))returnradius第二种情况,用if代替max的Aera2类classArea2(Rect):def__init__(self,x,y,w,h):super().__init__(x,y,w,h)@propertydefcenter(self):returnself.x+self.w/2,self.y+self.h/2defnorm(self,pos):"""定义距离"""ifabs(pos[0]-self.center[0])>self.wph*abs(pos[1]-self.center[1]):返回abs(pos[0]-self.center[0])返回self.wph*abs(pos[1]-self.center[1])def__contains__(self,pos):pos_norm=self.norm(pos)ifpos_norm>self.radius:returnFalseelse:returnTrue第三种,都是用if判断Aera0类Area0(Rect):def__init__(self,x,y,w,h):super().__init__(x,y,w,h)@propertydefcenter(self):returnself.x+self.w/2,self.y+self.h/2def__contains__(self,pos):ifpos[0]self.x+self.w:returnFalseifpos[1]self.y+self.h:returnFalsereturnTrue在代码组织上,我更喜欢Area1类。你可以仔细看看这些代码之间的区别。我们只是使用一些不同的操作来查看python代码的性能特征。Testcode:if__name__=="__main__":area0=Area0(10,10,10,10)area1=Area1(10,10,10,10)area2=Area2(10,10,10,10)pos1=(1,2)defcheck1():foriinrange(100000):ifpos1inarea1:passt0=time.time()foriinrange(100000):ifpos1inarea0:passt1=time.time()foriinrange(100000):ifpos1inarea1:passt2=time.time()foriinrange(100000):ifpos1inarea2:passt3=time.time()check1()t4=time.time()dt=t1-t0print("Area0:",t1-t0,"rate:","1")print("Area1:",t2-t1,"rate:",(t2-t1)/dt)print("Area2:",t3-t2,"rate:",(t3-t2)/dt)print("check1:",t4-t3,"rate:",(t4-t3)/dt)看一下运行结果:Area0:0.017013072967529297rate:1Area1:0.23638176918029785rate:13.894125395891141Area2:0.12012887001037598rate:7.060974242551641check1:0.22240829467773438rate:13.072787914459486同样以判断的情况为基准1。发现判断确实是最快的,而且快的不是一点点,也就是我觉得很好的一个算法,但是性能比傻傻的判断慢了一个数量级。为什么?因为我用的是python自带的函数,所以在这个函数中用到了比较基础的操作。另外值得一提的是,这里的check1只是对area1调用的一个简单的函数封装,但是速度有所提升。当然不是很大。至少它表明,在复杂的问题中,将它们封装在函数中是有优势的。结论和性能提升技巧赋值、判断和基本操作是同一个数量级的。我的电脑是十亿分之一秒量级,可以随意使用。尝试编写尽可能多的函数,不要太多。这又是一个矛盾。一些与循环无关的操作,尽量放在循环之外。对于复杂的流程,用函数封装不会损失性能。python的性能是一个很复杂的问题。希望这篇文章能给大家一些初步的感觉。我们常常认为很漂亮的算法和组织形式,其实都是以牺牲性能为代价换来的。在和很多程序员交流的过程中,我发现大部分的性能问题都取决于语言本身,所以大部分的抱怨都会针对某一种语言。但是,你要知道,对于人工智能这样的东西,没有任何强大的语言可以取代算法。算法会给软件带来前所未有的改进。这是程序员的修身环保精神,因为我们消耗的是计算资源。有人说性能考虑应该占70%,也有人认为性能不重要,还是选择优雅,你怎么看?文渊网,仅供学习,侵删。学习Python的路上肯定会遇到困难,不要慌张,我这里有一套学习资料,包括40+电子书,800+教学视频,涉及Python基础、爬虫、框架、数据分析、机学习等等,别怕你学不会!https://shimo.im/docs/JWCghr8...《Python学习资料》关注公众号【蟒圈】,每日优质文章推送。