Python 中函数参数是值传递还是引用传递?
Python 的参数传递是"对象引用传递":可变对象(列表/字典)在函数内修改会影响外部,不可变对象(数字/字符串)不会。本文详解原理和注意事项。
Python 既不是纯值传递也不是纯引用传递,而是"对象引用传递"(pass by object reference)。函数接收到的是对象的引用:可变对象(list/dict)在函数内修改会影响外部;不可变对象(int/str/tuple)不会,因为不能原地修改。
不可变对象:函数内修改不影响外部
def modify(x):
x = x * 2 # 创建新对象,不影响外部
print(x) # 10
y = 5
modify(y)
print(y) # 5(没变)
x = x * 2 创建了新的 int 对象,x 指向新对象,外部的 y 仍指向原来的 5。
可变对象:函数内修改影响外部
def modify(lst):
lst.append(4) # 原地修改,影响外部
print(lst) # [1, 2, 3, 4]
my_list = [1, 2, 3]
modify(my_list)
print(my_list) # [1, 2, 3, 4](被修改了!)
append() 是原地修改,同一个列表对象被函数和外部共享。
常见踩坑:默认参数
# 危险!默认参数是可变对象
def add_item(item, target=[]):
target.append(item)
return target
add_item(1) # [1]
add_item(2) # [1, 2]——上次的列表被复用了!
# 正确写法
def add_item(item, target=None):
if target is None:
target = []
target.append(item)
return target
如何避免函数修改原始数据
def safe_modify(lst):
new_lst = lst.copy() # 创建副本
new_lst.append(4)
return new_lst
original = [1, 2, 3]
result = safe_modify(original)
print(original) # [1, 2, 3](未被修改)
print(result) # [1, 2, 3, 4]
常见问题(FAQ)
Q:如何判断一个对象是不是可变的?
A:可变:list、dict、set、bytearray;不可变:int、float、str、tuple、frozenset。也可以看有没有 __hash__——不可变对象通常可哈希。
Q:函数内重新赋值会影响外部吗?
A:不会。x = 新值 只是让局部变量指向新对象,外部的变量不受影响。只有调用可变对象的原地修改方法(append/extend/pop 等)才影响外部。
Q:tuple 里的 list 能修改吗?
A:能。tuple 不可变是指不能增删元素,但如果元素是 list,这个 list 本身可以被修改。这是深浅拷贝需要注意的地方。