函数参数传递方式:值传递还是引用传递?
写代码时,调用函数传参是家常便饭。但你有没有遇到过这样的情况:在函数里改了参数的值,外面的变量却没变?或者相反,一改全跟着变了?这背后其实就是函数参数传递方式在起作用。
值传递:传的是“副本”
值传递就像复印文件。你把一份文档交给同事处理,他拿去复印了一份,在复印件上涂涂改改,原件还在你手里,一点没动。编程中,基本数据类型比如整数、浮点数、布尔值,大多采用值传递。
看个例子:
def modify_value(x):
x = x + 10
print("函数内 x =", x)
num = 5
modify_value(num)
print("函数外 num =", num)
输出结果是:
函数内 x = 15
函数外 num = 5
函数里的 x 是 num 的一个副本,改 x 不会影响原始的 num。
引用传递:传的是“地址”
引用传递更像是共享网盘里的文件链接。你把一个文档的链接发给同事,他打开后直接编辑,你再打开看,内容已经变了。这是因为你们操作的是同一个文件。在 Python、Java 等语言中,列表、字典、对象这类复杂类型,传递的是引用。
举个实际场景:你在写一个统计用户订单的小程序,要把订单列表传给一个处理函数。
def add_discount(orders):
for i in range(len(orders)):
orders[i] = orders[i] * 0.9 # 打九折
order_list = [100, 200, 150]
add_discount(order_list)
print(order_list)
输出是:[90.0, 180.0, 135.0]。你会发现原列表被修改了。因为传进去的是列表的引用,函数内操作的就是原始数据。
Python 的特殊之处:对象引用传递
Python 官方说法是“按对象引用传递”。听起来有点绕,其实可以理解为:参数传递时,传的是对象的引用,但这个引用本身是值传递。也就是说,你不能改变引用指向的对象地址,但可以修改对象的内容。
比如:
def reassign_list(lst):
lst = [4, 5, 6] # 重新赋值,指向新对象
def append_to_list(lst):
lst.append(4) # 修改原对象内容
my_list = [1, 2, 3]
reassign_list(my_list)
print(my_list) # 输出 [1, 2, 3],没变
append_to_list(my_list)
print(my_list) # 输出 [1, 2, 3, 4],变了
第一个函数里,lst = [4,5,6] 让局部变量指向了一个新列表,不影响外面的 my_list。而第二个函数通过 append 修改了原列表的内容,所以外部能感知到。
实际开发中的注意事项
在团队协作或维护老代码时,如果不清楚参数会不会被修改,很容易埋坑。比如你传了个配置字典进去,结果函数偷偷加了字段,后续逻辑可能就乱了。
解决办法很简单:如果不想让原数据被改,传参前先复制一份。
config = {'timeout': 30, 'retries': 3}
process_config(config.copy()) # 传副本,保护原数据
或者用 list(my_list)、dict.copy()、copy.deepcopy() 根据需要选择深浅拷贝。
理解参数传递方式,不只是为了应付面试,更是写出稳定、可维护代码的基础。下次传参前,不妨多想半秒:我传的到底是值,还是引用?