基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Python 是传值还是传引用?

知识点图片

这是一个非常经典且容易让人困惑的问题。

严格来说,Python 既不是纯粹的“传值”(Pass by Value),也不是纯粹的“传引用”(Pass by Reference)。

准确的定义是:传对象引用(Pass by Object Reference),或者叫 按赋值传递(Pass by Assignment)

核心概念:变量是标签,不是盒子

在 C 或 Java 等语言中,变量通常被视为存储数据的“盒子”。但在 Python 中,变量是指向对象的“标签”或“引用”

当你调用函数时,Python 实际上是将对象的引用(内存地址)传给了函数的参数。

函数内部的行为取决于你传递的对象类型是“可变”还是“不可变”:


1. 传递不可变对象(Immutable Objects)

类型: int, float, str, tuple, bool

表现: 看起来像“传值”。

因为这些对象本身不能被修改,如果你在函数内部试图修改它,Python 会创建一个新的对象,并将参数变量指向这个新对象。外部的变量不会受到影响。

python
def modify_number(x):
    print(f"函数内修改前 x 的地址: {id(x)}")
    x = 100  # 这是一个赋值操作,创建了新对象 100,x 指向了它
    print(f"函数内修改后 x 的地址: {id(x)}")
    print(f"函数内 x 的值: {x}")

a = 10
print(f"调用前 a 的地址: {id(a)}")
modify_number(a)
print(f"调用后 a 的值: {a}")  # a 仍然是 10

结果: 外部变量 a 没有改变。


2. 传递可变对象(Mutable Objects)

类型: list, dict, set, 自定义类的实例

表现: 看起来像“传引用”。

因为对象是可变的,函数内部的参数变量和外部变量指向同一个内存地址。如果你通过这个引用修改了对象的内容(例如 append, pop, 修改字典键值),外部变量也会随之改变。

python
def modify_list(lst):
    print(f"函数内 lst 的地址: {id(lst)}")
    lst.append(999)  # 在原对象上进行修改
    print(f"函数内 lst 的值: {lst}")

my_list = [1, 2, 3]
print(f"调用前 my_list 的地址: {id(my_list)}")
modify_list(my_list)
print(f"调用后 my_list 的值: {my_list}") # my_list 变成了 [1, 2, 3, 999]

结果: 外部变量 my_list 被改变了。


3. 特殊情况:可变对象被重新赋值

这是最容易混淆的地方。即使传递的是可变对象(如列表),如果你在函数内部对其进行了重新赋值(使用 = 号),那么连接就会断开,外部变量不会改变。

python
def reassign_list(lst):
    # 这里使用了 = 号,意味着 lst 被指向了一个全新的列表对象
    lst = [100, 200] 
    print(f"函数内 lst 的值: {lst}")

my_list = [1, 2, 3]
reassign_list(my_list)
print(f"调用后 my_list 的值: {my_list}") # 仍然是 [1, 2, 3]

总结

Python 的参数传递机制只有一种:传对象引用

  • 如果对象是不可变的(Immutable): 你无法修改原对象,任何修改操作都会生成新对象。(效果类似传值)
  • 如果对象是可变的(Mutable):
    • 如果你修改对象的内容(如 append),外部会变。(效果类似传引用)
    • 如果你重写/赋值变量(如 x = []),外部不会变。
00:00
00:00