
python 所有变量都存储对象引用,但赋值操作仅复制当前引用值;修改变量本身(如 `a = a.next`)不会影响其他已持有原引用的变量。
在链表合并等指针操作中,一个常见困惑是:为什么执行 curr.next = list1 后再执行 list1 = list1.next,curr.next 仍指向原来的节点,而非更新后的 list1.next?这并非 Python 的“按值传递”或“按引用传递”的二元误解,而是源于对 变量绑定(binding) 与 对象状态修改(mutation) 的本质区分。
✅ 核心原则:赋值改变的是变量的引用目标,而非已有引用的“连接关系”
考虑以下简化模型:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
# 构造链表: 1 → 2 → 3
n1 = ListNode(1)
n2 = ListNode(2)
n3 = ListNode(3)
n1.next = n2
n2.next = n3
curr = ListNode(0) # 哨兵节点
list1 = n1 # list1 指向 n1
curr.next = list1 # ✅ 复制 list1 当前引用 → curr.next 现在也指向 n1
list1 = list1.next # ✅ 修改 list1 变量:让它指向 n2(即 n1.next)此时:
- curr.next 仍严格指向 n1(内存地址未变);
- list1 已重新绑定到 n2;
- n1.next 本身仍是 n2(对象状态未被修改),所以 curr.next.next 依然可访问 n2。
? 关键区别:list1 = list1.next 是重绑定变量(re-binding),而 list1.next = ... 才是修改对象属性(mutation)。前者不影响其他变量;后者会影响所有引用该对象的变量。
? 对比你提供的列表示例,理解为何行为“看似矛盾”
a = [] b = [1] a = b # a 和 b 现在绑定到同一列表对象 b.append(2) # ✅ 修改对象内容 → a 也能看到变化(因为 a、b 指向同一可变对象) print(a) # [1, 2]
这里 b.append(2) 并未改变 b 变量本身的引用,而是调用了列表对象的 append 方法——就地修改了该对象的内部状态。由于 a 和 b 仍绑定到同一个列表对象,因此 a 观察到了变化。
立即学习“Python免费学习笔记(深入)”;
但在链表代码中:
- curr.next = list1 → 让 curr.next 绑定到 list1 当前所指对象(如 n1);
- list1 = list1.next → 让 list1 这个变量改绑到另一个对象(n2),与 curr.next 无关。
二者不冲突,只是作用层面不同:一个是变量级重绑定,一个是对象级状态变更。
⚠️ 注意事项与最佳实践
- 不要混淆 x = x.attr 和 x.attr = y:前者重绑定变量 x,后者修改 x 所指对象的属性。
- 在链表/树遍历中,常用 node = node.next 推进指针,这安全且高效——它不会破坏已建立的链接结构。
- 若误写为 curr.next.next = list1.next(试图“跳过”节点),则可能引发逻辑错误或循环引用;务必明确每一步是改谁的引用、还是改谁的状态。
- 对于不可变对象(如 int, str, tuple),即使重绑定变量,也无法触发“共享修改”,因为它们不支持 in-place mutation。
✅ 总结
Python 中没有“传引用”或“传值”的底层语义,只有统一的引用语义(reference semantics):变量是标签,赋值是贴标签,修改对象属性是改标签所指之物的内容。curr.next = list1 贴了一个新标签到 list1 当前所指对象;list1 = list1.next 是把 list1 这个标签撕下来,贴到另一个对象上。两者互不干扰——这正是链表合并算法能正确构建新链、而不意外切断旧链接的根本原因。










