其它答主说的都挺好的,有一些小技巧我也是初次看到,学习了。不过有一些概念性的东西并不是“坑”,所以想澄清一下。
一、有关is和id()的现象,并不违背Python设计,不属于BUG或坑
1、整数复用问题(整数 is 整数)
is与id操作符有一些奇怪的现象,典型的像这样:
>>> a=56
>>> b=56
>>> a is b
True
>>> a=257
>>> b=257
>>> a is b
False
>>> a=-5
>>> b=-5
>>> a is b
True
>>> a=-6
>>> b=-6
>>> a is b
False
问题可以描述为:如果a和b都指向整数对象,且a==b,那么a is b是真还是假?
答:当a和b在某个范围内时,a is b == True,当超出这个范围时,a is b == False
可以用上述代码简单测出这个范围,比如我当前环境下令a is b==True的范围是
为什么呢?
在进一步讨论之前要先确认一点——在实践中,不应该对整数或字符串使用 is 操作符判断相等性! 判断相等应当使用“==”运算符。
如果你还是好奇背后的原理,也不难解释:在Python中,所有的对象都是引用类型的,所以理论上程序中用到了1000个数字,就得创建1000个数字对象。字符串也是同理。
问题是,现实中的程序,大量数字对象会集中在较小的范围内,比如从-3到100就特别常用。那么完全可以将较小的数字事先全部创建好。之后如果要用到1、2、3、55之类的常用数字,就自动使用已经创建好的数字对象即可,不需要重新创建这些数字对象。
只不过这个范围应加以控制,不能事先把 -1亿~+1亿 的数字创建好,那样内存占用太多了。在Python的源码中,是用两个常量指定这一范围,比如常见的-5~100,或者-10~256等。
关键是:在编译Python时可以随意修改整数复用范围,所以每个系统中的范围都可以不同。所以我们写程序不应假设当 a==b==5 时,a is b 一定为真。总之,就根本不应该用is对数字或字符串做相等性判断。
具体原因这本书讲的非常清晰(可惜已经绝版):
2、字符串复用问题(字符串 is 字符串)
理解了整数对象复用的原理,再理解字符串复用也非常容易。常用的较短字符串,出现一次以后就会被记录下来。
比如初次创建"hello"时,做个速查索引。下次再用到"hello"就尽量复用之前创建过的那个"hello"。
注意~ 这里说的是“尽量”,有时程序员加加减减让底层无法判断你到底要哪个字符串,就导致创建了多个"hello"对象的情况。但出现这种情况只是让优化失效了而已,并不会造成BUG~~~ 只要记住用等号判断相等的原则就行。
3、浮点数相等性判断,与浮点数做字典的Key
浮点数的情况与int和string的情况不太一样。关键是:在几乎所有编程语言中,都不能直接用等号判断两个浮点数相等。
背后的原因是:现代浮点数的标准规范一般用的是IEEE 754或类似标准,在标准中已经明确说明,在加减乘除、网络传输等情况下,出现误差是正常现象。比如:
>>> n=0
>>> for i in range(1000):
... n+=0.001
...
>>>
>>> n
1.0000000000000007
>>> n==1
False
将0.001加1000次,结果不等于1。
现在几乎所有的主流语言,都需要使用类似Abs(f1 - f2) < epsilon的方法判断浮点数的相等性,这不是语言的问题,而是浮点数标准本身就是这样规定的。
推论:既然浮点数判断相等不靠谱,那用浮点数做字典的key当然更不靠谱了。 比如用5.5作为字典的Key,那么之后用 (0.1+5.2+0.2) 去查询,就不敢保证能对应到5.5。对浮点数有一定了解以后,自然就不敢写这种代码了~~
2、某些Python的语法现象确实是坑
1、当默认参数为可变对象时,调用函数可能会把默认参数给改掉:
>>> def f(args=[]):
... args.append(3)
... print(args)
...
>>> f()
[3]
>>> f()
[3, 3]
>>> f()
[3, 3, 3]
>>> # 显然默认参数本身变了
虽然Python有充分的理由如此设计,但“默认参数能修改”这个问题还是会在实践中带来麻烦。
2、局部变量作用域不明确:
>>> for i in range(5):
... pass
...
>>> print(i)
4
在现代语言中,一般在循环体中声明的变量 i,在循环外是未定义的。但python并不遵循这一惯例,你能在循环外得到循环时最后的 i 值。这样的设计有时比较方便,但有时也会带来混淆。
还有一些小瑕疵不再赘述,可以参考其它答主的回答。
3、真正的“坑”在语法以外的地方
在2020年的今天,Python可以说的火的如日中天。大量各种领域的项目会考虑使用Python作为开发语言。
事实证明Python虽然是一种比较灵活的动态类型语言,但只要合理运用、制定并遵守合理的开发规范,就完全能够胜任中大型项目。
潜在的问题在于,当你的项目获得巨大成功、获得海量访问的时候,Python的性能问题会带来一些麻烦。对于Web应用和App后台来说,可能要稍微多花点钱租用更多的云服务器;而对于实时类应用来说(比如网络游戏),性能问题可能更复杂。
当然,初期也不需要想的太远。Python在快速开发、快速迭代方面有巨大优势。对某些小团队来说,如果用了Python,可能会给未来带来一些麻烦;而不用Python,则连成功的机会都会错过。
4、
总之,绝大多数情况下,只要按照常规方式编程,不要使用一些奇技淫巧,就不会踩坑。一些容易犯错的语法问题也都可以通过网络搜索解决。
Python不存在“不为人知”的坑,因为那些坑不为人知。