知行LEARNING HANDBOOK
目录 · N04 Python 对象模型、int 与 CPython 实现
学习手册/计算机基础/对象模型与内存布局
N048 分钟更新于 2026-08-20

Python 对象模型、int 与 CPython 实现

沿着 Python 对象模型、int 与 CPython 内部结构,连接语言现象和底层实现。

pythonobject-modelintcpythonpylongobjectimmutabilitysmall-integer-cache

建议先了解

  • Python 函数与类
  • C 结构体基础

1. 这篇笔记解决什么问题

本篇把本次对话中最核心的一条 Python 主线串起来:

Everything is object
    ↓
模块、函数、类、方法都有属性
    ↓
int 也是对象
    ↓
int 不可变
    ↓
CPython 用 PyLongObject 实现整数
    ↓
小整数缓存与对象生命周期

学完后应能回答:函数和方法为什么有属性;Python 中什么不是对象;x = 10 到底是什么意思;x = x + 1 时原来的 10 怎么处理;Python 为什么能表示超大整数;CPython 为什么预创建小整数;安装 Python 后这些 C 实现代码在哪里。

2. 前置知识

待补充:descriptor、引用计数、Python allocator 的完整实现。

3. 核心概念

3.1 Everything is object

Python 中绝大多数可作为运行时“值”存在的东西都统一为对象:

整数       → int 对象
字符串     → str 对象
列表       → list 对象
函数       → function 对象
类         → type/class 对象
模块       → module 对象
类型本身   → 对象

但语法结构本身不是普通对象,例如:

if / for / while
+ / - / =
调用括号 ()
属性访问符 .
赋值语句本身

x = 10 中:

  • x 是名字;
  • = 是赋值语法;
  • 10int 对象;
  • 执行后形成名字到对象的绑定。

3.2 函数和方法也有属性

def add(a, b):
    """Add two numbers."""
    return a + b

add 是函数对象,可以访问:

add.__name__
add.__doc__
add.__module__
add.__annotations__
add.__code__

普通 Python 函数对象还可以附加自定义属性:

add.author = "Kening"

类中:

class Person:
    def hello(self):
        return "hello"

Person.hello 是类上的函数;p.hello 通常是绑定到实例的 bound method,可理解为保存:

p.hello
├── __self__ → p
└── __func__ → Person.hello

3.3 int 是类型对象,10 是实例

type(int)
# <class 'type'>

type(10)
# <class 'int'>

isinstance(10, int)
# True

关系:

       type
        ↑
       int
        ↑
  10, 20, -5 ...

3.4 名字绑定与不可变性(Immutability)

x = 10

更适合理解为:

名字 x ─────→ int 对象 10

而不是 C 式“变量盒子里直接存一个固定宽度数值”。

Python int 是不可变对象:

x = 10
x = x + 1

不是把 10 原地改成 11,而是计算结果后让 x 重新绑定。

3.5 为什么整数不可变

本次对话中讨论了三个主要动机:

  1. 多个名字共享同一个整数对象时,不会产生原地修改带来的副作用;
  2. 整数适合稳定哈希,可作为 dict / set key;
  3. 不可变对象可以安全复用与缓存。

数学直觉也一致:数字 10 本身不会“变成 11”,只是变量从表示 10 改为表示 11。

3.6 Python int 是任意精度整数

Python int 不固定为 32/64 bit:

x = 10 ** 1000

仍可以表示,只要实现与内存资源允许。

CPython 底层整数类型使用 PyLongObject

3.7 CPython 中的大整数表示

对话中以常见 CPython 构建为例,大整数拆成多个 digit/limb。概念公式:

N=d0+d1B+d2B2+N = d_0 + d_1 B + d_2 B^2 + \cdots

常见配置中 B 可对应 2^30,因此可写作:

N=d0+d1230+d2260+N = d_0 + d_1 2^{30} + d_2 2^{60} + \cdots

概念结构:

PyLongObject
├── Python 对象头
├── 符号 / digit 数量等信息
└── digits[]
    ├── d0
    ├── d1
    └── ...

[!WARNING] PyLongObject 的精确字段、digit 位宽和标志布局是 CPython 版本/构建细节,不是 Python 语言规范。

3.8 小整数缓存

CPython 会复用一批高频小整数对象。对话中以 CPython 3.14.6 源码为例讨论了这一机制。

目的不是“不占内存”,而是:

少量固定内存
    ↓ 换取
减少频繁的小整数对象分配、初始化、回收

不可变性使共享安全。

4. 直观理解

Python 的核心模型可简化成:

名字
 ↓ 绑定
对象
 ↓
拥有类型和属性

对于整数:

原来:
x → 10

执行 x = x + 1

结果:
      10
x →   11

变化的是 x 的绑定,不是 10 的值。

5. 工作原理

5.1 原整数对象如何处理

一般语义:

旧对象
  │
  ├── 还有引用 → 继续存在
  └── 没有引用 → 实现可以回收

例如:

x = 1000
y = x
x = x + 1

之后:

y → 1000
x → 1001

对于 CPython 缓存/immortal 的小整数,旧对象可能仍被 runtime 长期保留。

5.2 CPython 执行整数运算的概念链

Python 源码
  ↓
Python bytecode
  ↓
CPython interpreter
  ↓
整数操作实现
  ↓
Objects/longobject.c 等已编译 C 代码
  ↓
PyLongObject 结果

运行程序时不会重新读取或编译 longobject.c;它早已在构建 CPython 时编译成机器代码。

5.3 安装 Python 后源码在哪里

安装后的 Python 通常包括:

Python installation
├── python / python3 二进制可执行文件
├── 标准库纯 Python 文件
├── 编译扩展模块
├── site-packages
└── 其他运行时文件

完整 CPython C 源码并不要求随普通安装保留,研究实现时应查看 CPython 源码仓库。

本次对话提到的关键源码路径:

Include/object.h
Include/cpython/longintrepr.h
Objects/longobject.c
Include/internal/pycore_long.h
Include/internal/pycore_runtime_structs.h
Python/bytecodes.c

6. 示例

示例 1:绑定而不是修改

x = 10
y = x
x = 20
print(y)  # 10

说明:x = 20 没有修改 10 对象。

示例 2:任意精度

x = 2 ** 1000
print(x.bit_length())

说明:语言层的整数宽度不受普通 CPU 寄存器宽度直接限制。

示例 3:整数方法

(10).bit_length()
(13).bit_count()
(1024).to_bytes(2, byteorder="big")
int.from_bytes(b"\x04\x00", byteorder="big")

这把 int 与二进制位、字节序联系起来。

7. 代码、命令或公式

整数不同字面量:

10
0b1010
0o12
0xA

它们表示相同整数值。

可用于观察对象:

import sys

x = 10
print(type(x))
print(id(x))
print(sys.getsizeof(x))
print(dir(x))

[!WARNING] id()sys.getsizeof() 和 small-int identity 现象都不能直接上升为跨 Python 实现的语言规范。

8. 容易混淆的概念

概念 A概念 B核心区别
名字对象名字绑定对象,不等于对象本身
int10前者是类型对象,后者是实例
修改名字绑定修改不可变对象x = 11 是重新绑定
Python intC intPython 任意精度,C 常见为固定宽度
functionbound methodbound method 额外绑定了实例
small-int cachePython 语言语义前者是 CPython 实现优化
C 源码已安装解释器源码用于构建,运行时执行的是编译产物

9. 常见误区

误区:函数只是“代码”,不算对象

错误原因:把可执行代码和承载它的运行时 function object 混为一谈。

正确理解:函数在 Python 中可以被赋值、传递、拥有属性。

如何验证:type(f)f.__name__、把函数放进 list。

误区:x += 1 把整数对象 10 变成 11

错误原因:套用可变对象或 C 变量槽模型。

正确理解:int 不可变,名字重新绑定到结果对象。

如何验证:让 xy 先绑定同一个整数,只更新 x

误区:小整数缓存不占内存

错误原因:把“复用”理解成“不存储”。

正确理解:缓存本身占固定内存,目的是减少反复创建/销毁成本。

误区:Python 大整数由 CPU 无限宽整数寄存器直接支持

错误原因:把语言抽象能力等同硬件能力。

正确理解:CPython 用多个 digit 软件实现大整数算术。

10. 与其他知识的关系

11. 可以亲手完成的验证

实验目标:观察绑定、对象大小和方法属性。

import sys

x = 10
y = x
print(id(x), id(y))

x = 10 ** 1000
print(sys.getsizeof(x))
print(x.bit_length())


def f():
    pass

print(type(f))
print(f.__name__)

预期:整数值越大,对象所需存储可能增加;函数拥有自己的类型和属性。

实验不能证明:CPython 所有版本都使用相同 small-int 范围或同样的 PyLongObject 字段布局。

12. 尚未解决的问题

  • PyObject_HEAD 精确布局尚未逐字段展开。
  • descriptor 如何把类上的函数变成 bound method 尚未深入。
  • CPython 引用计数、immortal object 与垃圾回收的完整关系尚未整理。
  • 大整数乘法、除法采用哪些算法及规模阈值未讨论。
  • CPython 不同平台安装目录结构尚未分别整理。

13. 自测问题

  1. “Everything is object” 为什么不意味着 if+ 也是普通对象?
  2. p.hello 为什么能自动得到 self
  3. x = 10 中,x10 分别是什么?
  4. 为什么 int 设计为 immutable?
  5. Python int 如何超过 CPU 原生整数宽度?
  6. CPython 为什么缓存小整数,而不缓存所有整数?
  7. 为什么安装 Python 后通常看不到 longobject.c
参考答案
  1. Everything is object 描述运行时值模型,关键字和运算语法属于语言语法层。
  2. 实例属性访问会产生绑定方法,保存实例与原函数的关联。
  3. x 是名字,10int 实例对象。
  4. 可避免共享对象被意外修改、保持哈希稳定并允许安全缓存复用。
  5. CPython 把大整数拆成多个内部 digit/limb,用软件算法计算。
  6. 小整数高频且范围有限;所有整数集合无界,无法全部缓存。
  7. C 源码已在 CPython 构建阶段编译,普通安装主要提供运行构建产物。

14. 一句话总结

Python 通过统一对象模型把整数、函数、类和模块都纳入“对象—类型—属性”体系;int 再以不可变、任意精度语义存在,而 CPython 用 PyLongObject、多 digit 算术和小整数复用实现这套抽象。