找回密码
立即注册
搜索
发回帖 发新帖

6499

积分

0

好友

820

主题
发表于 3 小时前 | 查看: 4| 回复: 0

面试官问:“Python 的变量里保存的是什么?a = b 之后,修改 b 会影响 a 吗?”

很多人会套用 C 语言的理解:变量是一块内存,赋值就是把值拷进去。用这个模型看 Python,很快会遇到解释不了的现象——比如改了一个列表,另一个变量也跟着变。这道题真正要讲清楚的是:Python 变量是名字,对象才是数据,赋值是在名字和对象之间建立绑定。

简要回答

Python 中,变量是一个名字,它保存的是对某个对象的引用,而不是对象本身。对象是真正存在于内存中的数据,有自己的身份、类型和值。

执行 a = b 时,Python 不会复制对象,而是让名字 a 也绑定到 b 当前指向的对象。如果之后通过 b 原地修改这个对象,比如对列表 append(),a 看到的也是修改后的结果;如果让 b 重新绑定到别的对象,比如 b = [],那么 a 不受影响。

所以,“修改 b 会不会影响 a”,要先分清是修改对象,还是让名字指向新对象。

详细回答

赋值做了什么:先有对象,再绑定名字

执行赋值语句时,Python 先计算右侧表达式,得到一个对象,再把左侧的名字绑定到这个对象上:

price = 100
discount = price
price = 80
print(discount)  # 100

第一行,名字 price 绑定到整数对象 100。第二行,discount 绑定到 price 当前指向的同一个对象。第三行,price 改为绑定整数对象 80,discount 仍然指向 100。

赋值只改变名字的指向,不会修改原对象。这也是为什么 Python 的名字没有固定类型:price 可以先指向整数,再指向字符串,类型属于对象,不属于变量。

Python变量名字绑定与重新绑定示意图

图左边是名字所在的命名空间,右边是对象。上半部分的重新绑定只改了箭头;下半部分的原地修改改的是对象,所以两个名字都能看到变化。

多个名字共享同一个可变对象

当共享的是可变对象时,绑定模型的影响就很明显:

cart = ["book"]
saved = cart
saved.append("pen")
print(cart)           # ['book', 'pen']
print(cart is saved)  # True

saved = cart 没有生成新列表,cart 和 saved 是同一个列表的两个名字。append() 在原列表上添加元素,所以通过 cart 也能看到 "pen"。is 返回 True,证明它们是同一个对象。

如果想要一个独立的列表,需要显式创建新对象,比如 saved = cart.copy() 或 saved = list(cart)。

对象的三要素:身份、类型和值

每个对象都有三样东西:

  • 身份:对象在生命周期内唯一且不变的标识,用 id() 查看,用 is 比较。在 CPython 中,id() 返回的是对象的内存地址,但语言只保证它在对象存活期间唯一。
  • 类型:决定对象支持哪些操作,用 type() 查看,创建后不会改变。
  • 值:对象保存的数据。可变对象的值可以改,不可变对象的值不能改,这部分展开见《Python 中的可变对象和不可变对象有什么区别?》。

名字本身不属于这三要素。同一个对象可以有零个、一个或多个名字指向它。

del 删除的是名字,不是对象

很多人以为 del 会直接销毁对象,其实它解除的是名字的绑定:

item = ["x"]
other = item
del item
print(other)  # ['x']

del item 之后,名字 item 不再可用,但列表还被 other 引用,所以对象依然存在。对象什么时候被回收,由解释器的内存管理决定;在 CPython 中,主要依靠引用计数,当没有任何引用指向对象时,它会被回收。

知识拓展

面试官可能追问:

Q1:Python 函数传参是值传递还是引用传递?

两个说法都不够准确,更准确的叫法是 对象共享传递:调用函数时,形参绑定到实参指向的同一个对象。函数内原地修改可变对象会影响外部,重新绑定形参则不会。它和本文讲的赋值绑定是同一套规则。

Q2:a is b 和 a == b 有什么区别?

is 判断两个名字是否指向同一个对象,== 判断两个对象的值是否相等。值相等的两个列表可以是不同对象。具体规则和常见误用见《is 和 == 有什么区别?》。

Q3:为什么 a = 256; b = 256 时 a is b 可能是 True?

在 CPython 中,-5 到 256 的小整数会被预先创建并复用,所以两个名字可能指向同一个对象。这是实现层面的优化,换一个版本或实现结果可能不同。比较数值应该用 ==,不能依赖这种缓存。

Q4:Python 代码运行时,名字存在哪里?

名字存在 命名空间 中,比如模块的全局命名空间、函数调用时的局部命名空间,可以近似理解为“名字到对象”的映射。代码在执行前会被编译成字节码,执行过程见《Python 程序如何运行?》。




上一篇:Python可变对象与不可变对象:x += 1到底改没改对象
下一篇:GPT-6 Astra理论物理评测:98%解题率反向纠错人类专家
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-19 06:24 , Processed in 0.077104 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表