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

Python程序员常犯的10个错误,这些坑你踩过吗?

时间:2023-03-25 20:13:49 Python

关于PythonPython是一种解释型、面向对象、具有动态语义的高级编程语言。它内置高级数据结构,结合动态类型和动态绑定的优点,这使得它在快速应用程序开发中非常有吸引力,可以用作脚本或胶水语言来连接现有的组件或服务。Python支持模块和包,它们鼓励程序模块化和代码重用。关于本文Python易于学习的语法可能会导致Python开发人员(尤其是那些刚接触编程的开发人员)忽略它的一些微妙之处并低估该语言的功能。考虑到这一点,本文提供了一个“Top10”错误列表,即使是高级Python开发人员有时也很难捕捉到这些错误。1:滥用表达式作为函数参数的默认值Python允许函数参数默认可选值。尽管这是该语言的一个重要特性,但它可能会导致与某些可变默认值的混淆。例如,看看这个Python函数的定义:\>>>deffoo(bar=\[\]):bar.append("baz")returnbar一个常见的错误是认为可选参数不是每个都提供的时间函数可选参数将被设置为调用时的默认指定值。例如,在上面的代码中,人们可能期望重复调用foo()(即没有明确指定bar参数)将始终返回“baz”,因为每次调用foo()都假设(没有设置bar参数)bar设置为[](即空列表)。\>>>foo()\["baz"\]\>>>foo()\["baz","baz"\]\>>>foo()\["baz","baz","baz"\]是吗?为什么每次调用foo()时都会将默认值“baz”附加到现有列表而不是创建一个新列表?回答默认参数是在定义时评估的(比如当你第一次导入模块时)。因此,bar参数被初始化为其默认值(即一个空列表),即当foo()首次被定义时,但是当foo()被调用时(即当bar参数未指定时)它将继续使用bar参数初始化。这是一个常见的解决方法:\>>>deffoo(bar=None):ifbarisNone:bar=\[\]bar.append("baz")returnbar\>>>foo()\["baz"\]\>>>foo()\["baz"\]\>>>foo()\["baz"\]小编补充:还有一个常见的例子就是默认参数是表达式,例如:importdatetimedeflog(message,time=datetime.datetime.now()):print("{0}:{1}".format(time,message))期望每次记录不同的时间,但是未能这样做,记录是同时的。运行以下代码:importtimelog('message1')time.sleep(1)log('message2')time.sleep(1)log('message3')time.sleep(1)结果如下如下:2017-07-2520:53:20.225000:message12017-07-2520:53:20.225000:message22017-07-2520:53:20.225000:message32:类变量的错误使用考虑以下示例:\>>>classA(object):x=1\>>>classB(A):pass\>>>classC(A):pass\>>>printA.x,B.x,C.x111没有任何例外。\>>>B.x=2\>>>printA.x,B.x,C.x121也和你想要的一样。\>>>A.x=3\>>>printA.x,B.x,C.x323结果似乎有点出人意料。我们只改了A.x,为什么C.x也改了?在Python中,类变量在内部作为字典处理,遵循经常引用的方法解析顺序(MRO)。所以在上面的代码中,由于没有找到C类中的x属性,所以会去查找它的基类(虽然Python支持多重继承,但是上面的例子中只有A)。也就是说,类C没有自己的x属性,它是独立于A的。因此,C.x实际上是对A.x的引用。3:为except指定错误的参数假设你有如下一段代码:\>>>try:l=\["a","b"\]int(l\[2\])exceptValueError,IndexError:pass回溯(最近调用最后):文件“”,第3行,在IndexError:列表索引超出范围这里的问题是except语句不接受以这种方式指定的异常列表。相反,在Python2.x中,使用语法exceptException,e将异常对象绑定到第二个_可选_参数(本例中为e)以供以后使用。所以,在上面的例子中,异常IndexError并没有被except语句捕获,而是在它绑定到一个名为IndexError的参数时抛出。在except语句中捕获多个异常的正确方法是将第一个参数指定为包含要捕获的所有异常的元组。而且,为了代码的可移植性,使用as关键字,因为Python2和Python3都支持这种语法:\>>>try:l=\["a","b"\]int(l\[2\])except(ValueError,IndexError)ase:pass\>>>4:不懂Python的作用域Python是基于LEGB进行解析的,LEGB是Local,Enclosing,Global,Built-in的缩写。看起来像是“知文义”吧?其实Python还是有一些需要注意的,先看下面这段代码:\>>>x=10\>>>deffoo():x+=1printx\>>>foo()Traceback(最近调用最后一次):文件“”,第1行,在文件“”,第2行,在fooUnboundLocalError:localvariable'x'referencedbeforeassignment这里出了什么问题?出现上述问题是因为当你给作用域内的变量赋值时,Python会自动将其视为当前作用域的局部变量,从而隐藏外层作用域的同名变量。当在以前工作代码的函数体中某处添加赋值语句后出现UnboundLocalError时,很多人都会感到惊讶。这个问题更常见,尤其是当开发人员使用列表时。考虑以下示例:\>>>lst=\[1,2,3\]\>>>deffoo1():lst.append(5)\#没问题...\>>>foo1()\>>>lst\[1,2,3,5\]\>>>lst=\[1,2,3\]\>>>deffoo2():lst+=\[5\]\#。..但有一个问题!\>>>foo2()Traceback(最近调用最后一次):文件“”,第1行,在文件“”,第2行,在fooUnboundLocalError:之前引用了局部变量“lst”任务嗯?为什么foo2报错,foo1却没事?原因与前面的示例相同,但更难以捉摸。foo1没有分配给lst,但是foo2分配了。你知道,lst+=[5]是lst=lst+[5]的缩写,我们试图分配给lst(Python将其视为局部变量)。此外,我们对lst的赋值是基于lst本身(它再次被Python视为局部变量),但它尚未定义。因此错误!5:迭代时修改列表下面代码中的问题应该相当明显:\>>>odd=lambdax:bool(x%2)\>>>numbers=\[nforninrange(10)\]\>>>foriinrange(len(numbers)):ifodd(numbers\[i\]):delnumbers\[i\]Traceback(mostrecentcalllast):文件"",line2、inIndexError:listindexoutofrange在迭代时从列表或数组中移除元素对于任何有经验的开发人员来说都是众所周知的错误。虽然上面的示例很明显,但许多高级开发人员在更复杂的代码中无意中这样做了。幸运的是,Python包含了大量简洁优雅的编程范式,如果使用得当,可以极大地简化和提炼代码。这样做的好处是可以得到更简单、更精简的代码,也可以更好地避免程序在迭代时修改一个列表的bug。一种这样的范例是列表理解。此外,列表理解对于这个问题特别有用,通过更改上面的实现,我们得到了一段优秀的代码:\>>>odd=lambdax:bool(x%2)\>>>numbers=\[nforninrange(10)\]\>>>numbers\[:\]=\[nforninnumbersifnotodd(n)\]\>>>numbers\[0,2,4,6,8\】看完前半部分,这些坑你踩了吗?最后,非常感谢您阅读我的文章!有什么问题可以后台私信我,我会很乐意解答。