Python 中的一些坑和坑和坑和弱智设计

一个不怎么文雅的吐槽,因为给 Python 气坏了,Python 傻逼语言。

前段时间给胡俊峰当 Python 的助教(工资还没发),发现这个语言的若干坑。

命名空间

其实这个机制很好的,垃圾就垃圾在 Python 创建引用的机制。

一个等号就是创建引用。创建引用就是新变量。

比如下面这段代码:

x = 1
def func():
    x = 2

print(x)

当然是输出 1。

你说不对啊,为什么变量的值没改呢?因为 Python 根本没有变量,只有对象和对于对象的引用。你在函数内创建了一个对于 2 的引用,引用名称叫 x,离开了函数空间这个引用就被销毁了。你说你外面还有个 x 啊?Python: 我管你呢。

如果要 Python “发现”一个外部的引用,那么需要关键字 global,至少也得是个 nonlocal

可变与不可变

对于不可变对象,比如整数、字符串、元组这些对象,+= 是怎么发挥作用的呢?

x += 1 将被翻译成 x = x + 1。换句话说,重新创建一个引用,引用名称是 x,新对象是 x + 1

你说 C++ 不这样啊?那关我 Python 什么事呢。

根本原因是不可变对象只有成员函数 __add__,而没有自增运算 __iadd__。只有对于可变对象,x += 1x = x + 1 才是有区别的。

>>> a = 'aba'
>>> id(a)
140367141023280
>>> a += 'a'
>>> id(a)
140367141023336
>>> a = [1, 2]
>>> id(a)
140367140999880
>>> a += [3]
>>> id(a)
140367140999880
>>> a = a + [3]
>>> id(a)
140367141001736

一些神奇问题

同学 A 提问:我 list 怎么没了?

>>> a = [1, 2, 3, 4]
>>> b = [5, 6, 7, 8]
>>> c = zip(a, b)
>>> c
<zip object at 0x7fa9c593dbc8>
>>> sorted(c)
[(1, 5), (2, 6), (3, 7), (4, 8)]
>>> list(c)
[]

zip 对象是一个 iterator。只有 next(),迭代完了就没了。

同学 B 提问:Python 是不是不许我 dp 啊?

>>> n = 3
>>> m = 5
>>> dp = [[0] * n] * m
>>> dp
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> dp[0][1] = 1
>>> dp
[[0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0]]

Python: 真抱歉,list 的乘法是浅复制。

dp = [[0] * n for _ in range(m)]

上面是正确写法。


Hermera