Python 的陷阱

Ruijun Gao @ Nov 23, 2020

本文介绍了 Python 中常见的一些陷阱。

喋喋不休生成器

l = [0]
l += l # ok, l == [0, 0]
l += (e for e in l) # infinite loop

一条蛇试图吞掉自己的尾巴。

合并同类项字典

d = dict()
d[     0] = 'a' # d == {0: 'a'}
d[   0.0] = 'b' # d == {0: 'b'}
d[0 + 0j] = 'c' # d == {0: 'c'}

数,生而平等。dict 根据 ==(__eq__) 来判断是否为相同的键,而 0 == 0.0 == 0 + 0j

铺张浪费缝合怪

nums = [0, 1, 2, 3, 4, 5, 6]
head, tail = nums[:3], nums[3:]
# head == [0, 1, 2] and tail == [3, 4, 5, 6]
it = iter(nums)
list(zip(it, head)) # [(0, 0), (1, 1), (2, 2)]
list(zip(it, tail)) # [(4, 3), (5, 4), (6, 5)]

请将迭代器视为一次性用品。zip 迭代器会在 zip 的任一迭代器参数停止时停止,但它无法预知哪一迭代器会率先停止。于是,它将轮流地检查 ithead, 而 it 作为第一个迭代器参数,将会率先消费掉一个元素 3,然后 zip 迭代器才会检查到 head 已经停止(即调用 next(head) 抛出 StopIteration)。

夹带私货默认值

def with_default(val=0, ref=[]):
    val += 1
    ref.append(0)
    print(f'{val=}, {ref=}')

with_default() # val=1, ref=[0]
with_default() # val=1, ref=[0, 0]
with_default() # val=1, ref=[0, 0, 0]

在为可变的引用类型的参数设置默认值时,应当配合使用 Noneif 或进行拷贝,即

def with_default(val=0, ref=None):
    if ref is None:
        ref = []
    val += 1
    ref.append(0)
    print(f'{val=}, {ref=}')

with_default() # val=1, ref=[0]
with_default() # val=1, ref=[0]
with_default() # val=1, ref=[0]