一、补码:负数在计算机里如何表示?

计算机底层只认识二进制。

整数通常以固定长度存储,比如 32 位或 64 位。

对于有符号整数,最高位通常表示符号:

0 表示非负数
1 表示负数

现代计算机通常使用补码表示负数。

以 8 位为例:

0000 0001  表示  1
1111 1111  表示 -1
1111 1110  表示 -2

补码的好处是:计算机可以用同一套加法规则处理正数和负数。

例如:

0000 0001   1
1111 1111  -1
---------
0000 0000   0

最高位溢出被丢弃后,结果就是 0。

这就是补码的优雅之处。


二、Python 的变量:名字是标签,对象才是本体

Python 里的变量,不是一个固定盒子。

它更像是贴在对象上的标签。

a = 10
b = a
a = 20

执行过程可以理解为:

1. 创建整数对象 10
2. a 指向 10
3. b 也指向 10
4. 创建整数对象 20
5. a 改为指向 20
6. b 仍然指向 10

所以:

print(a)  # 20
print(b)  # 10

Python 中更准确的理解是:

变量名绑定对象

而不是:

变量名就是一块固定内存

三、可变对象和不可变对象

Python 对象可以大致分为两类。

不可变对象:

int
float
str
tuple
bool

可变对象:

list
dict
set

不可变对象不能在原地修改。

例如:

a = 10
a = 20

这里不是把原来的整数 10 改成了 20,而是让 a 指向了新的整数对象 20


可变对象可以在原地修改。

a = [1, 2, 3]
b = a

a.append(4)

print(b)

输出:

[1, 2, 3, 4]

原因是 ab 指向同一个列表对象。

append() 修改的是这个列表对象本身,所以两个变量都能看到变化。

这也是二维列表浅拷贝问题的根源。


四、C++ 的变量:更像固定盒子

C++ 的普通变量更接近“固定内存盒子”。

int a = 10;
int b = a;
a = 20;

可以理解为:

1. 给 a 分配一个 int 盒子,里面放 10
2. 给 b 分配另一个 int 盒子,把 a 的值复制过去
3. 把 a 盒子里的内容改成 20
4. b 的盒子不受影响

所以 C++ 里的普通变量更贴近硬件内存模型。

Python 偏“对象引用和名字绑定”。
C++ 偏“内存位置和数值写入”。


五、Python vs C++:两种变量哲学

可以这样对比:

对比项

Python

C++

变量像什么

标签、便利贴

盒子、不动产

赋值的本质

名字绑定到对象

把值写入内存区域

对象管理

解释器和垃圾回收负责更多

程序员控制更多

灵活性

相对低

性能和控制力

相对弱

Python 的好处是写起来灵活、抽象程度高。

C++ 的好处是更贴近硬件,性能和控制力更强。

理解这点之后,很多语言差异就会变得非常清晰。


六、PyPy3 和 CPython:为什么 OJ 上表现不一样?

刷题时经常会看到:

Python3
PyPy3

大多数情况下,Python3 指的是 CPython。

CPython 是最常见、最标准的 Python 实现。

它的特点是:

  • 启动快

  • 兼容性好

  • 行为稳定

  • 解释执行


PyPy3 的特点是 JIT,也就是即时编译。

它会在程序运行时观察哪些代码执行得最频繁,然后把这些热点代码编译成更高效的机器码。

所以在大量循环、大规模计算时,PyPy3 可能比 CPython 快很多。


但是 PyPy3 不一定总是赢。

因为 JIT 本身也有成本:

  • 需要观察热点代码

  • 需要编译

  • 需要维护机器码缓存

  • 启动和热身成本更高

  • 内存占用可能更大

所以小数据量题目里,PyPy3 可能还没热身完,程序已经结束了。

这时 CPython 反而可能更快、更省内存。

可以这样记:

CPython 像轻装短跑选手
PyPy3 像带装备的长跑选手

短跑时,装备是负担。
长跑时,装备才发挥优势。


七、大模型 Self-Attention:为什么长上下文贵?

大语言模型的核心机制之一是 Self-Attention。

它的基本直觉是:

每个 token 都要和上下文里的其他 token 建立关系

如果上下文长度是 N,那么 token 两两关系大致是:

N × N

所以标准 Self-Attention 的复杂度通常是:

O(N²)

注意,这是平方复杂度,不是指数复杂度。

但平方复杂度已经非常可怕。

当上下文长度从 1,000 增加到 10,000,长度增加 10 倍,注意力计算量可能接近增加 100 倍。

这就是为什么长上下文模型非常吃算力。


八、KV Cache:大模型里的空间换时间

大模型生成文本时,是一个 token 一个 token 生成的。

如果每生成一个新 token,都重新计算前面所有 token 的信息,会非常浪费。

所以工程上会使用 KV Cache。

可以粗略理解为:

把之前已经算过的 Key 和 Value 缓存起来,后面直接复用

这样生成新 token 时,就不必从头重复计算整个上下文。

优点:

  • 生成速度更快

  • 减少重复计算

  • 长文本生成更现实

代价:

  • 占用大量显存

  • 上下文越长,占用越大

  • batch 越大,占用越大

所以 KV Cache 的本质就是:

空间换时间

这和算法里的缓存、哈希表、动态规划数组,本质上是同一种思想。


九、把所有知识串起来:时间、空间、引用、缓存

无论是 Python 变量、C++ 内存,还是大模型 KV Cache,它们背后的核心问题都很相似。

你永远可以问这几个问题:

数据在哪里?
谁指向它?
有没有复制?
修改的是对象,还是换了引用?
为了更快,付出了多少空间?
为了更省空间,又牺牲了多少时间?

这些问题,就是理解计算机系统的底层抓手。


十、总结

这一篇的核心不是某个具体语法,而是建立底层世界观。

知识点

本质

补码

用统一加法规则处理正负数

Python 变量

名字绑定对象

可变对象

可以原地修改

不可变对象

修改时通常创建新对象

C++ 变量

更接近固定内存盒子

CPython

稳定、轻量、解释执行

PyPy3

JIT 加速,适合长循环

Self-Attention

标准复杂度通常是 O(N²)

KV Cache

用显存换生成速度

真正理解计算机,不只是记住“这个 API 怎么用”。

更重要的是看见代码背后的东西:

内存、对象、引用、复制、缓存、复杂度,以及时间和空间之间永远存在的交换。

当你开始用这些问题去看代码,就说明你已经从“会写语法”,进入了“理解系统”的阶段。