Python 中的函数参数传递:传值还是传引用? 传参行为
在 Python 中,函数参数传递的方式是很多程序员初学时常常困惑的地方。它与许多编程语言不同,因为 Python 既不完全是“传值调用”,也不完全是“传引用调用”。为了清楚地理解这一点,我们需要明确 Python 中的参数传递机制。
1. Python 参数传递的本质:传对象引用
Python 的函数参数传递方式被称为 “传对象引用”(pass-by-object-reference)或 “传值引用”(pass-by-assignment)。这意味着,当一个对象作为参数传递给函数时,实际上传递的是对象的引用,而不是对象的副本。具体来说,传递的是对象在内存中的地址(即引用),而不是对象本身。通过这种机制,函数可以访问并修改传入的对象,但行为是否改变对象取决于对象的类型(是否是可变类型)。
不同类型的对象:可变与不可变
Python 中的对象可以分为两类:
- 不可变对象(Immutable objects):如整数(
int
)、浮动数(float
)、字符串(str
)、元组(tuple
)等。 - 可变对象(Mutable objects):如列表(
list
)、字典(dict
)、集合(set
)等。
这些类型的对象在函数调用时有着不同的行为,影响着我们是否能够修改原始对象的值。
2. 不可变对象:传递对象的引用,但不能修改
对于不可变对象,虽然我们传递的是对象的引用,但由于不可变对象的特性,在函数内部修改这些对象的值会导致新的对象的创建,而不会改变原始对象。
示例:
def modify_value(a):
a = 10 # 创建新的整数对象并赋值给a
x = 5
modify_value(x)
print(x) # 输出 5,原始x值未改变
解释:
- 在
modify_value
函数中,我们尝试将a
设置为10
。由于int
是不可变类型,a
并不会直接修改原始的x
变量,而是创建了一个新的整数对象10
并赋值给a
。 - 因此,
x
保持不变,输出为5
。
为什么不可变对象不能修改?
不可变对象一旦创建,它的值就无法更改。这意味着对于不可变对象,任何改变都相当于创建了一个新的对象,并将引用指向这个新对象。因此,传递给函数的引用指向的对象本身无法改变,但引用可以指向一个新的对象。
3. 可变对象:传递引用并修改原始对象
对于可变对象,函数内部可以通过引用修改对象的内容,因为它们是可变的。这样,对原始对象的修改将影响到外部变量。
示例:
def modify_list(lst):
lst.append(4) # 修改原始列表对象
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # 输出 [1, 2, 3, 4],原始列表被修改
解释:
my_list
传递给modify_list
函数时,lst
指向与my_list
相同的内存地址。当我们在函数内对lst
进行append(4)
操作时,实际上是在修改原始对象。- 因此,
my_list
被直接修改,输出为[1, 2, 3, 4]
。
为什么可变对象会改变?
可变对象的值可以在原地修改。由于传递的是对象的引用,函数对这些对象的操作直接影响原始对象。因此,对于列表、字典等可变对象,函数调用过程中对其内容的修改是可见的。
4. 赋值操作与修改对象
值得注意的是,如果在函数内部重新赋值参数变量(例如 lst = [7, 8, 9]
),并不会影响原始对象的引用。这是因为,重新赋值操作会让函数内部的参数变量指向一个新的对象,而不会修改原始对象。
示例:
def modify_reference(lst):
lst = [7, 8, 9] # 重新赋值,lst指向新的对象
original_list = [1, 2, 3]
modify_reference(original_list)
print(original_list) # 输出 [1, 2, 3]
解释:
- 在
modify_reference
函数中,lst
被重新赋值为[7, 8, 9]
。这时,lst
不再指向原始的original_list
,而是指向一个新的列表对象。 - 由于重新赋值仅影响函数内部的
lst
变量,original_list
保持不变。
5. 总结:传对象引用,但行为依赖于对象的类型
不可变对象:当不可变对象(如
int
,str
,tuple
)作为参数传递时,函数内无法修改原始对象。任何修改都会创建新的对象,原始对象保持不变。可变对象:当可变对象(如
list
,dict
,set
)作为参数传递时,函数内可以修改原始对象的内容,影响外部的变量。如果在函数内重新赋值给参数变量(例如lst = [...]
),则仅会改变函数内的引用,而不会影响外部对象。
结论
Python 中的参数传递是 传对象引用(pass-by-object-reference),但行为依赖于对象的类型。对于不可变对象,传递的引用无法改变对象的内容;而对于可变对象,引用可以直接修改对象的内容。因此,在编写 Python 函数时,理解这一点对于避免意外的副作用和编写高效代码至关重要。