首页 新闻 会员 周边 捐助

python的变量名绑定过程

0
[已关闭问题] 关闭于 2025-08-30 16:16
  • 生活里的“绑”
    把玩具用绳子系在书包上:
    以后只要拎书包,玩具就跟着走;把绳子剪断(解绑),玩具就和书包没关系了。

    

  • 编程里的“绑”
    把名字(变量名)用“看不见的绳子”系在一个对象上:
    以后只要用这个名字,就自动拿到这个对象;把名字重新赋给别的对象,就是“剪断绳子再系到新的玩具上”。
a = [1, 2, 3]   # 把名字 a 绑到列表对象 [1, 2, 3]
b = a           # 把名字 b 也绑到**同一个**对象
a = 100         # 把名字 a 剪断,重新绑到整数对象 100
_java_python的主页 _java_python | 小虾三级 | 园豆:984
提问于:2025-08-30 16:10
< >
分享
所有回答(1)
0

运行时的数据结构

每个线程(或模块)都有一个 局部变量字典,Python 里就是 PyDictObject(哈希表)。
键:PyUnicodeObject(变量名字符串)
值:PyObject*(指向真实对象的指针)。

/* CPython 3.11 Objects/frameobject.c 简化 */
typedef struct _frame {
PyObject *f_locals; /* dict: 变量名 -> PyObject* */
...
} PyFrameObject;

 

1.2 一条赋值语句的旅程

源码:a = [1, 2, 3]
  1. 解释器读到 BUILD_LIST,在堆里新建一个 PyListObject,地址 0x7f8e4c0b9c80
  2. STORE_NAME a,在当前栈帧的 locals 哈希表插入一条记录:
    key: "a" → value: 0x7f8e4c0b9c80
  3. 以后只要解析到标识符 a,就查这张哈希表,取出 PyObject* 指针即可。
栈帧 locals dict
┌---------┐
│  "a" ---┼---┐
└---------┘   │
              ▼
        [PyListObject 0x7f8e4c0b9c80]
        +--+--+--+--+--+--+--+--+
        |ob_refcnt|ob_type|...|items|

重绑(a = 100)只是改哈希表的 value 字段,从列表地址改成整数对象地址。
没有复制对象,也没改对象本身,只是改字典里的一条指针。

具体过程:

源码 → 编译器(compile.c)把 a = [1, 2, 3] 翻译成 字节码

LOAD_CONST   0 (1)
LOAD_CONST   1 (2)
LOAD_CONST   2 (3)
BUILD_LIST   3
STORE_NAME   0 (a)   <-- 关键指令
字节码 → 解释器(ceval.c)执行到 STORE_NAME 时

PyObject *v = POP();            // 弹出刚建好的 list
PyObject *name = GETITEM(names, oparg);  // 常量池里拿到字符串 "a"
PyObject_SetItem(f->f_locals, name, v);  // 把 "a" -> list 指针 写入 locals dict
以后读到 a 时,字节码是
LOAD_NAME 0 (a)
解释器再查同一张哈希表,取出对应 PyObject* 压栈使用。
因此:
编译阶段:变量名只作为符号放进字节码的常量池,不做地址分配。
运行阶段:解释器用这些符号当 key,去当前命名空间(locals、globals、builtins 三张哈希表)里查 value,取到的就是对象指针。
换句话说:
“变量名”只是字节码里的一个整数索引,真正绑定动作发生在运行时哈希表的一次 PyDict_SetItem/PyDict_GetItem。

“贴标签 / 重新绑定”在 Python 里就是对命名空间字典做一次插入或覆盖——
第一次贴标签:dict 里新增一条 key → value;
重新绑定:同一个 key,value 换成新的对象地址,key 本身不变。
所以:
不是改 key 的值(key 始终是那个字符串 "a"),
只是改这条记录里的 value 字段(指针指向另一个对象)。
例如:
b=1
a=b
其中
b = 1**当前命名空间(locals dict)**里插入/更新一条记录:
key: "b" → value: 指向整数对象 1 的指针。
如果 "b" 原来就存在,只是把 value 换成新指针;不存在就新建条目。
a = b
解释器 先从命名空间里读出 "b" 对应的指针,
然后把这条指针再写一条新记录:
key: "a" → value: 同一个指向 1 的指针。
命名空间里于是出现两条独立条目:

"a" -> PyLongObject(1)
"b" -> PyLongObject(1)   # 指向同一个 1 对象
所以:
第一次写 b = 1 是“插入或覆盖”;
第二次写 a = b 是“读取 + 再插入一条新键值对”,不会把 "a""b" 绑成同一条记录。

 

_java_python | 园豆:984 (小虾三级) | 2025-08-30 16:16
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册