问题详情
在 Python 中,无论是可变数据类型(如列表、字典)还是不可变数据类型(如整数、字符串、元组),多个变量引用指向同一个对象都是允许且常见的行为。
这是 Python 引用模型的核心特性:变量只是对象的标签(名字),而不是盒子。多个标签可以贴在同一个物体上。
Python 中的变量本质上是指向内存中对象的指针。当你执行
b = a时,Python不会复制对象,而是让b也指向a所指向的那个对象。此时,该对象的“引用计数”加 1
对于可变对象(如
list,dict,set),多个引用指向同一个对象时,修改其中一个变量,会影响所有指向该对象的变量
对于不可变对象(如
int,str,tuple),多个引用指向同一个对象也是完全合法的。
区别在于:因为你不能修改不可变对象的内容,所以不会出现“改一个变全部”的副作用。如果你试图“修改”,实际上是创建了一个新对象,并将变量指向新对象,而其他变量依然指向旧对象。
# 不可变类型示例 str_a = "hello" str_b = str_a # str_b 和 str_a 指向同一个字符串对象 print(str_a is str_b) # True (确实是同一个对象) # 试图"修改" str_b (实际上是重新赋值) str_b = str_b + " world" # 观察 str_a print(str_a) # 输出: "hello" <-- str_a 没变 print(str_b) # 输出: "hello world" <-- str_b 指向了新创建的对象 # 验证它们现在指向不同的对象 print(str_a is str_b) # False
回答
在 Python 交互式命令行(REPL)中输入: python >>> a = 1000 >>> b = 1000 >>> a is b False 在交互式模式(REPL)中,你输入的每一行虽然被视为独立的“代码块”编译,但它们都运行在同一个全局作用域(Global Scope)中。 第一行 a = 1000:在全局命名空间中创建了一个变量 a,指向一个值为 1000 的整数对象。 第二行 b = 1000:在全局命名空间中创建了一个变量 b。 第三行 a is b:Python 去全局命名空间找 a 和 b,它们都还在。 结论:变量不会因为一行代码执行结束就消失,除非你显式删除它(del a)或者程序退出。 为什么 a is b 是 False?(整数缓存机制) 这才是问题的关键。Python 为了优化性能,会对小整数进行缓存(Interning),但对大整数通常不会。 小整数缓存范围:通常是 [-5, 256]。在这个范围内的整数,无论在哪里创建,Python 都会复用同一个对象。 整数行为:对于超出这个范围的整数(如 1000),Python 默认每次遇到字面量 1000 时,都会创建一个新的整数对象(除非在同一个代码块内被优化器识别)。
在 REPL 中的具体情况:
由于 REPL 是逐行执行的,每一行都是一个独立的代码块:
由于 REPL 是逐行执行的,每一行都是一个独立的代码块:
- 第一行
a = 1000:- 编译器看到字面量
1000。 - 创建一个整数对象
Obj_1(值 1000)。 - 让
a指向Obj_1。 - 该行执行结束。
- 编译器看到字面量
- 第二行
b = 1000:- 这是一个新的代码块。之前的优化上下文不复存在。
- 编译器再次看到字面量
1000。 - 创建另一个新的整数对象
Obj_2(值 1000)。 - 让
b指向Obj_2。
- 第三行
a is b:- 检查
a(指向Obj_1) 和b(指向Obj_2) 是否是同一个对象。 - 显然
Obj_1不是Obj_2。 - 返回
False。
- 检查
3. 对比:如果在脚本文件中运行
如果你把这三行写在一个
.py文件里运行:python
a = 1000 b = 1000 print(a is b)
在某些 Python 版本或编译器优化下,结果可能是
原因:整个脚本(或同一个函数内)被视为一个代码块。编译器在进行常量折叠(Constant Folding)优化时,可能会发现代码块内有两个相同的常量
True。原因:整个脚本(或同一个函数内)被视为一个代码块。编译器在进行常量折叠(Constant Folding)优化时,可能会发现代码块内有两个相同的常量
1000,于是让它们指向同一个对象以节省内存。但在 REPL 中,这种跨行的常量优化通常不会发生,因为每一行都是独立编译和执行的。
事实 1:变量 a 存储在全局命名空间中,只要不被覆盖或删除,它一直存在。
事实 2:1000 的对象没有被“丢弃”,a 依然紧紧引用着第一个 1000 对象。
事实 3:b 引用的是第二个新创建的 1000 对象。
真相:a is b 为 False 是因为 Python 没有缓存大整数,且 REPL 的逐行执行机制导致创建了两次对象,而不是因为对象被垃圾回收了。
版权:言论仅代表个人观点,不代表官方立场。转载请注明出处:https://www.stntk.com/question/2543.html
还没有评论呢,快来抢沙发~