KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

00 · 名字与对象 — keel 龙骨

类型:主题概念 · 应用课对应:01 章 · 数据结构:怎么选,以及可变性这颗雷

类型:主题概念 · 应用课对应:01 章 · 数据结构:怎么选,以及可变性这颗雷

这一页只回答四件事:它为什么会出现、它是什么、它长什么样、它怎么工作。

一、它为什么会出现

两个现象,都指向同一件事:

① b = a 之后改了 b,a 也跟着变了 —— 明明我只写了一次赋值
② 一个函数「什么都没传」,第二次调用却带着上一次的结果回来

程序里到处都要「给某个东西起个名字」:变量、参数、属性、容器里的元素。
如果每次赋值都把内容复制一份,两个名字之间就没有任何关系,上面两个现象都不该出现。

问题在于:Python 的 = 从来不是「把值放进去」,而是「让这个名字指向那个对象」。
复制还是不复制,由对象自己的性质决定,不由赋值这个动作决定。

二、它是什么

一句话:名字是贴在对象上的标签;赋值是把标签换个对象贴,不搬内容。

由此有两个直接后果,日常都要用:

它不是这三样东西——三个都会给出错误预测:

三、它长什么样

以下是 CPython 3.13.14(Windows)上的输出:

>>> a = [1, 2]
>>> b = a
>>> b.append(3)
>>> a, b
([1, 2, 3], [1, 2, 3])
>>> a is b
True
>>> c = [1, 2, 3]
>>> d = list(c)
>>> c is d, c == d
(False, True)
>>> s = "abc"
>>> id(s) == id(s + "d")      # 字符串:重绑,换了对象
False
>>> lst = [1]
>>> before = id(lst)
>>> lst.append(2)
>>> id(lst) == before         # 列表:就地改,还是同一个对象
True
>>> t = (1, 2)
>>> before = id(t)
>>> t += (3,)                 # 元组:看着像「改」,其实是重绑
>>> id(t) == before
False

读它有两个工具:id(obj) 问「这个对象此刻的编号」,obj is other 问「两个名字是不是同一个对象」。

用 is,不要用 id 比较。 对象一旦没人引用就会被回收,编号会被后来的对象拿去复用;
is 由语言直接回答「是不是同一个」,不受回收影响。

四、它怎么工作

从一次赋值到「改出效果」,是四步:

① 造对象   先算出等号右边的对象(可能复用已有的,也可能是新建的)
② 绑名字   让左边的名字指向它;这个名字原有的绑定被替换掉
③ 就地改   对可变对象调用改内容的方法(append / update / sort)——绑定没动
④ 重绑     对名字再赋一次值,或写成 s = s + x / lst = lst + [y] 这类「造新对象」的写法

判断一件事会不会波及别人,只需要问一句:这一步落在 ③ 还是 ④?

可变默认参数踩的全是 ③:默认值在函数定义时就造了一个对象,绑在函数上,
之后每次调用拿到的都是同一个标签指向的那个对象。

>>> def bad(acc=[]):
...     acc.append(1)
...     return acc
>>> bad(), bad(), bad()
([1, 1, 1], [1, 1, 1], [1, 1, 1])

三次返回的是同一个列表,所以打印出来三份长得一样,而不是 [1]、[1, 1]、[1, 1, 1]。

五、它会带出哪些概念

六、回到应用课

应用课里的那句话 现在你知道它为什么成立
"可变类型当默认参数会被所有调用共享" 默认值在定义时就造了一个对象并绑在函数上,每次调用拿到的是同一个对象
"改嵌套结构要用 deepcopy" list(x) 只重绑外面那层,内层还是同一批对象,改内层谁都能看见
"元组是不可变的" 准确说法是没有就地改的接口;t += (3,) 不报错,因为它做的是重绑不是改

这一页改变了你的哪个判断:以后看到「改了一处、另一处也变了」,第一件事是问这两个名字是不是指向同一个对象(is),而不是去怀疑缓存或全局状态;看到 def f(x=[])、def f(x={}),第一件事就是把它改掉。

自测题

  1. a = [1]、b = a、b += [2] 之后 a 是什么?把 [1] 换成 (1,) 再算一次,结果为什么不同?
  2. 为什么 bad() 连续三次返回同一个列表?要让每次调用都拿一份新的,有哪两种改法?

进入 keel 阅读