摘要:Python 是一个简洁优雅的高级编程语言,它容易上手的同时,也隐藏了一些难以理解和甚至反人类直觉的坑。本文列出一些我们线上代码实际遇到过的一些编码问题。
一、不要混用 Tab 和空格
如上图中的代码,return n那行代码的是Tab缩进,而其他行是4 个空格,当编辑器设置Tab显示宽带为 4 个空格宽时,就会出现逻辑和直觉相悖的情况。
解决办法:
要求团队成员遵循 PEP 8 代码规范 ,统一采用 4 个空格缩进代码
项目根目录配置 .editorconfig ,Python 文件采用 4 个空格缩进
配置 IDE 编辑器,显示特殊控制字符
二、不要使用可变默认参数
class Blog(object):
def __init__(self, blog_id, tags=[]):
self.blog_id = blog_id
self.tags = tags
blog1 = Blog(1)
blog2 = Blog(2)
blog1.tags.append('Python')
blog2.tags.append('Java')
print(blog1.tags, blog2.tags) # Output: ['Python', 'Java'], ['Python', 'Java']
Python 函数的默认参数在定义时确定,如果调用函数时不显示传递默认参数,那么每次调用都相同对象。如果该对象时可变类型(比如list),则容易出现上面的问题。
解决办法:
不要使用可变变量做默认参数,可以使用None,然后代码里面判断如果是None就初始化一个新对象。
class Blog(object):
def __init__(self, blog_id, tags=None):
self.blog_id = blog_id
self.tags = [] if tags is None else tags
三、注意 is 和 == 的区别
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
>>> a, b = 257, 257
>>> a is b
True
is 和 == 的区别:
is 运算符检查两个运算对象是否引用自同一对象(即,它检查两个对象的 id 是否相同)
== 运算符比较两个运算对象的值是否相等
256是一个已经存在的对象,而257不是。为什么?
实际编程过程中,-5 到 256 之间的数字经常会用到,Python 为了避免频繁创建和销毁内存,对 -5 到 256 之间的所有整数会预先分配一个全局整数对象数组,
当需要创建一个该范围内的整数时,Python 会直接返回现有对象的引用。其他编程语言(比如 Java)也有类似的缓存机制。
对于字面量字符串也有类似缓存优化,另外也可以手动使用 intern 将字符串缓存 。
四、生成器执行时间的差异
array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
输出:
>>> print(list(g))
[8]
原因:
(x for x in array) 返回的是一个 生成器 (而 [x for x in array] 返回的是一个 list)
在一个生成器表达式里,in 的操作是在声明时求值的,而 if 是在运行期求值的
所以 x 枚举的是声明时的列表 [1, 8, 15],而 if 判断的是被重新赋值列表 [2, 8, 22]
对于 [1, 8, 15] 中的每个数,只有 8 在新的 array 中个数大于 0,因此生成器只生成 8
五、for 循环中修改循环变量
如下代码,在for循环体修改循环变量i的值:
for i in range(4):
print(i)
i = 10
输出:
0
1
2
3
原因:
Python 的for循环机制是每次迭代到下一项的时候都会解包并分配一次;即range(4)生成的四个值在每次迭代的时候都会解包一次并赋值给i;所以赋值操作i = 10对迭代没有影响。
六、一个 += 的谜题
>>> t = (1, 2, [30, 40])
>>> t[2] += [50, 60]
到底会发生下面 4 种情况中的哪一种?
t 变成 (1, 2, [30, 40, 50, 60])。
因为 tuple 不支持对它的元素赋值,所以会抛出 TypeError 异常。
以上两个都不是。
1 和 2 都是对的。
你可能会选 2,但答案是 4:
>>> t = (1, 2, [30, 40])
>>> t[2] += [50, 60]
Traceback (most recent call last):
File "", line 1, in
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [30, 40, 50, 60])
s[a] = b背后的字节码:
>>> dis.dis('s[a] += b')
1 0 LOAD_NAME 0(s)
3 LOAD_NAME 1(a)
6 DUP_TOP_TWO
7 BINARY_SUBSCR ➊
8 LOAD_NAME 2(b)
11 INPLACE_ADD ➋
12 ROT_THREE
13 STORE_SUBSCR ➌
14 LOAD_CONST 0(None)
17 RETURN_VALUE
➊ 将 s [a] 的值存入 TOS(Top Of Stack,栈的顶端)。
➋ 计算 TOS += b,这一步能够完成,是因为 TOS 指向的是一个可变对象。
➌ s [a] = TOS 赋值。这一步失败,是因为 s 是不可变的元组。
PS,如果将t[2] += [50, 60]改成t[2] = t[2] + [50, 60]又该选哪个答案呢?
扩展资料