这篇文章收集了我在新手Python开发人员编写的代码中看到的非标准但偶尔微妙的问题。这篇文章的目的是帮助那些新手开发者度过编写难看的Python代码的阶段。为了照顾目标受众,本文做了一些简化(例如:在讨论迭代器时忽略了生成器和强大的迭代工具itertools)。对于那些新手开发人员,总是有使用反模式的理由,我已经尽可能地提供给他们。但通常这些反模式会使代码的可读性降低,更容易出现错误,并且不符合Python的编码风格。如果您正在寻找更多介绍性材料,我强烈推荐ThePythonTutorial或DiveintoPython。Python编程初学者喜欢用range来实现简单的迭代,获取迭代器长度范围内的迭代器中的每个元素:foriinrange(len(alist)):printalist[I]应该牢记:rangeandNotforsimpleiterationover序列。与那些用数字定义的for循环相比,用range实现的for循环虽然很自然,但是用在序列迭代时容易出bug,不如直接构造一个迭代器那么清晰:foriteminalist:printitemrange的滥用是容易引起意外的off-by-one错误,这通常是由于编程新手忘记了range生成的对象包括range的第一个参数而不是第二个参数,类似于java中的substring和许多其他此类函数。编程新手认为只有序列的末尾会产生错误:#Iteratetheentiresequencewrongmethodalist=['her','name','is','rio']foriinrange(0,len(alist)-1):#大小相差一(Offbyone)!printi,alist[I]范围使用不当的常见原因:1.需要在循环中使用索引。这不是使用索引的好理由:forindex,valueinenumerate(alist):printindex,value2。需要同时迭代两个循环,使用同一个索引得到两个值。这种情况下可以使用zip来实现:forword,numberinzip(words,numbers):printword,number3。需要迭代部分序列。在这种情况下,只能通过迭代序列切片来实现,注意添加必要的注释以表明意图:forwordinwords[1:]:#不包括第一个元素printword有异常:当你迭代一个大Sequence,切片操作带来的开销比较大。如果序列只有10个元素,那很好;但是当它有1000万个元素时,或者在对性能敏感的内部循环中进行切片时,开销就会变得非常大。在这种情况下,请考虑使用xrange而不是range[1]。除了用于遍历序列之外,range的一个重要用法是当你真的想生成一个数字序列而不是一个索引时:#Printfoo(x)for0<=x<5forxinrange(5):printfoo(x)如果你有这样的循环,正确使用列表理解:#Anugly,slowwaytobuildalistwords=['her','name','is','rio']alist=[]forwordinwords:alist.append(foo(word))您可以使用列表理解重写:words=['her','name','is','rio']alist=[foo(word)forwordinwords]为什么要这样做?一方面,你避免了正确的初始化列表可能导致的错误,另一方面,这样写的代码看起来干净整洁。对于那些有函数式编程背景的人来说,使用map函数可能感觉更熟悉,但在我看来这种方法不太像Pythonic。不使用列表解析的其他一些常见原因:1.需要嵌套循环。此时你可以嵌套整个列表理解,或者在列表理解的多行上使用循环:words=['her','name','is','rio']letters=[]forwordinwords:forletterinword:letters.append(letter)usingalistcomprehension:words=['her','name','is','rio']letters=[letterforwordinwordsforletterinword]注意:在具有多个循环的列表理解中,循环具有相同的order就像你没有使用列表理解一样。2.您需要在循环内进行条件测试。你只需要在列表理解中加入这个条件判断:words=['her','name','is','rio','1','2','3']alpha_words=[wordforwordinwordsifisalpha(word)]不使用列表推导的一个正当理由是你不能在列表推导中使用异常处理。如果迭代中的某些元素可能导致异常,则需要通过列表理解中的函数调用来转移可能的异常处理,或者根本不使用列表理解。性能缺陷在线性时间内检查内容从语法上看,检查列表或集合/字典是否包含元素表面上看是一样的,但在表面下却大不相同。如果需要反复检查某个数据结构是否包含某个元素,最好使用set而不是list。(如果要给要检查的元素关联一个值,可以使用dict;这样也实现了检查时间恒定。)#假设以列表开头lyrics_list=['her','name','is','rio']#避免下面的写法#LinearitycheckTimeprintword,"isinthelyrics"#***像这样写歌词,"isinthelyrics"[译者注:Python中set的元素和dict的键值都是hashable的,所以查找的时间复杂度是O(1)。]应该记住,创建集合会引入一次性开销,创建过程将花费线性时间,即使成员检查需要常数时间。所以如果你需要在循环中检查成员,最好花时间先创建集合,因为你只需要创建一次。#p#Variable泄漏循环通常,变量在Python中的范围比您在其他语言中的预期范围更广。例如:下面的代码不会被Java编译://Gettheindexofthelowest-indexediteminthearray//thatis>maxValuefor(inti=0;i
