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

Python中的三个“黑魔法”和“骚操作”

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

【.com原创稿件】本文主要介绍Python的高级特性:列表理解、迭代器和生成器,这些都是面试中经常被问到的特性。因为生成器实现了迭代器协议,所以可以通过列表理解生成。所以这三个概念作为一个章节来介绍,最容易让大家理解。如果你现在不明白也没关系。我不仅会让你知道以下内容但是,重要的是要知道为什么。Listcomprehension前几天有个HR找我聊listcomprehension。我说我经常使用这个公式从旧列表生成新列表。他直接拒绝了我,让我回去复习。,挺受打击的,所以决定帮大家复习一下。内容:列表推导:旧列表->新列表推导:字典推导集合推导1.列表推导:格式[expressionforvariableinoldlist]or[expressionforvariableinoldlistifcondition]例1:生成一个新列表,其名称长度大于3且首字母大写。names_old=['tom','amy','daming','lingling']names_new=[name.capitalize()fornameinnames_oldiflen(name)>3]print(names_new)输出:['Daming','Lingling']示例2:(大厂初级笔试题)生成一个元组列表,要求每个元素的形式为(0-5个偶数,0-10个奇数)。输出为:[(0,1),(0,3),(0,5),(0,7),(0,9),(2,1),(2,3),(2,5),(2,7),(2,9),(4,1),(4,3),(4,5),(4,7),(4,9)]for循环实现代码:new_list=list()foriinrange(5):#evenifi%2==0:forjinrange(10):#oddifj%2!=0:new_list.append((i,j))列表推导代码:new_list=[(i,j)foriinrange(5)forjinrange(10)ifi%2==0andj%2!=0]例3:(大厂小学笔试题)给出员工名单:employees_old=[{'name':"tmo","salary":4800},{'name':"amy","salary":3800},{'name':"daming","salary":7000},{'name':"lingling"","salary":5600}]如果员工工资大于5000则加200,否则加500,输出新的员工列表。列表理解:employees_new=[employee['salary']+200ifemployee['salary']>5000elseemployee['salary']+500foremployeeinemployees_old]print(employees_new)output:[5300,4300,7200,5800]发现结果是employeesalary列表,回头看代码,确实是把获取的数字给了列表,那么如何返回员工列表呢?让我们用普通的for循环来做个比较:foremployeeinemployees_old:ifemployee['salary']>5000:employee['salary']+=200else:employee['salary']+=500print(employees_old)output:[{'name':'tmo','salary':5300},{'name':'amy','salary':4300},{'name':'daming','salary':7200},{'name':'lingling','salary':5800}]是的,我们注意到两者的不同之处在于,在列表理解中,我们少了一个赋值(对字典元素的赋值),我们不能直接返回一个薪水值但是列表中的员工字典。正确的列表理解如下:employees_new=[{'name':employee['name'],'salary':employee['salary']+200}ifemployee['salary']>5000else{'name':employee['name'],'salary':employee['salary']+500}foremployeeinemployees_old]print(employees_new)2.字典推导:例1:dict_old={'a':'A','b':'B','c':'C','d':'C'}dict_new={value:keyforkey,valueindict_old.items()}print(dict_new)输出:{'A':'a','B':'b','C':'d'}3.集合理解:类似于列表理解典型用法:去重示例1:list_old=[1,2,3,5,2,3]set_new={xforxinlist_old}Print(set_new)output:{1,2,3,5}总结:至此,列表推导不就是一个创建列表的公式吗?除了简化代码,还装X?其实列表推导还有一个好处就是比for循环更高效,因为列表推导在执行的时候调用的是Python底层的C代码,而for循环是用Python代码执行的。哇~面试官最想听到的是第二点。迭代器因为迭代器协议对于很多人来说是一个比较抽象的概念,而生成器自动实现了迭代器协议,所以我们需要先解释一下迭代器协议的概念,以便更好的理解下面的Builder。能被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。迭代是一种访问集合元素的方式,而迭代器是一种记住遍历位置的对象。迭代器对象从集合的第一个元素开始访问,直到访问完所有元素。迭代器只能前进不能后退。迭代器协议:表示对象需要提供一个__next__()方法,该方法要么返回迭代中的下一项,要么引发StopIteration异常终止迭代。可迭代对象:它是一个实现了迭代器协议的对象。比如稍微熟悉Python的朋友应该都知道,Python的for循环不仅可以用来遍历列表,还可以用来遍历文件对象,如下图:withopen('F:/test/test.txt')asf:forlineinf:print(line)为什么在Python中,也可以使用for循环遍历文件?这是因为,在Python中,文件对象实现了迭代器协议,for循环并不知道它遍历的是一个文件对象,它只需要使用迭代器协议来访问该对象即可。正是因为Python的文件对象实现了迭代器协议,我们才能如此方便的访问文件,如下所示:withopen('F:/test/test.txt')asf:forlineinf:print(line)output:withopen('F:/test/test.txt')asf:print(dir(f))iterable绝对是一个迭代器吗?生成器是可迭代的,也是迭代器。列表是可迭代对象,但不是迭代器。list可以使用iter()函数将iterable变成迭代器list->iter(list)->iteratornext():例如:list1=iter([xforxinrange(10)])print(next(list1))print(next(list1))iterableobject:generatortuplelistcollectiondictionarystring如何判断一个对象是否可迭代?借助isinstance()函数:fromcollectionsimportIterableprint(isinstance([xforxinrange(10)],Iterable))#listprint(isinstance('helloworld',Iterable))#stringprint(isinstance(100,Iterable))#numberprint(isinstance((xforxinrange(10)),Iterable))#iterator输出:TrueTrueFalseTrue生成器生成器是Python最有用的特性之一,也是使用最少的Python特性之一。究其原因,主要是其他主流语言没有生成器的概念。也正是因为生成器是一个“新”的东西,所以一方面没有引起广大工程师的重视,另一方面也增加了工程师的学习成本,最终导致大家错过Python中如此有用的功能。我们已经知道可以直接通过列表理解来创建列表,但是由于内存限制,列表的容量必须是有限的。而且,创建一个包含100万个元素的列表,不仅占用大量存储空间,而且如果我们只需要访问前几个元素,后面的大部分元素占用的空间都会被浪费掉。因此,如果列表元素能够在循环的过程中按照一定的算法不断计算出后面的元素,就不需要创建一个完整的列表,这样也可以节省很多空间。在Python中,这种while循环计算的机制被称为生成器:generator。Python使用生成器提供对延迟操作的支持。所谓延迟操作,就是在需要的时候才产生结果,而不是马上产生。这也是生成器的主要好处。定义生成器Python以两种不同的方式提供生成器:方法一:使用列表推导生成器表达式:类似于列表推导(这也是我在第一节介绍列表推导的原因),但是,生成返回一个按需产生结果的对象,而不是而不是一次构建一个结果列表。例1:my_generator=(xforxinrange(5))#注意()不是[]print(my_generator)#我发现元素打印不出来print(type(my_generator))print(my_generator.__next__())#三获取元素方法的方法,注意输出结果print(next(my_generator))foriinmy_generator:print(i)#注意会抛出StopIteration异常#print(next(my_generator))print(next(my_generator))#generator只能遍历一次输出:Traceback(mostrecentcalllast):File"E:/pycharm/Leetcode/RL_Learning/printdata.py",line11,inprint(next(my_generator))StopIterationat0x0000000000513660>01234方法二:借助函数生成器函数:使用yield语句代替return语句返回函数结果。yield语句一次返回一个结果。在每个结果的中间,函数的状态被挂起,起到暂停的作用,以便下次可以从中断的地方继续执行。步骤:定义一个函数,使用yield关键字返回函数;调用函数并接收函数的返回值;返回的结果是生成器;使用next()或__nest__()获取所需的元素。例2:只要你的函数中出现了yield关键字,你的函数就不再是一个函数,而是一个生成器:#Fibonaccisequence:defib(length):#1。定义函数a,b=0,1n=0while11235注意:生成器只能遍历一次。调用函数时,并没有执行函数,而是直接生成了一个生成器。当调用next时,实际上执行了该函数,只是第一次调用next()方法是从函数头执行的。剩下的都是从上次执行到每次yield开始执行。总结:使用生成器后,代码行数少了。大家要记住,要想把代码写成Pythonic,在保证代码可读性的前提下,代码行数越少越好。合理使用生成器可以有效提高代码的可读性。只要大家完全接受生成器的概念,明白yield语句和return语句是一样的,也是有返回值的。然后,你就能明白为什么使用生成器比不使用生成器好,明白使用生成器真的可以让代码清晰易懂。在实际工作中,充分利用Python生成器,不仅可以减少内存占用,还可以提高代码的可读性。掌握生成器也是Python高手的标配。如果本文对您有帮助,别忘了关注、点赞或收藏支持~作者:臧元辉简介:就职于中科星途股份有限公司(北京)研发部后端技术组.个人擅长Python/Java开发,了解前端基础知识;精通MySQL、MongoDB、Redis;熟悉Linux开发环境,掌握Shell编程,有良好的Git源码管理习惯;精通Nginx、Flask、Swagger开发框架;有Docker+Kubernetes云服务开发经验。他对人工智能和云原生技术有更大的兴趣。【原创稿件,合作网站转载请注明原作者和出处为.com】