ZetCode

Python 按值传递与按引用传递

最后修改于 2025 年 5 月 16 日

本教程解释了 Python 如何处理可变和不可变类型,以及这对变量赋值和参数传递的影响。理解这些概念对于编写正确高效的 Python 代码至关重要。

可变与不可变类型

Python 类型被分为可变不可变,这决定了数据在变量之间如何被复制和共享。

特征 不可变类型 可变类型
可修改 否(创建新对象) 是(原地修改)
复制行为 实质上是按值传递 按引用传递
示例 intstrtuple listdictset

使用不可变类型赋值会创建独立的副本,而可变类型共享引用。但是,Python 总是传递引用——区别在于对象是否可以被原地修改。

不可变类型示例

当赋值时,不可变类型会创建独立的副本,因为修改会创建新对象而不是更改现有对象。

main.py
a = 10
b = a  # Both reference the same object

print(f"Original: a = {a}, b = {b}")
b = 20  # Creates new object, doesn't affect a
print(f"After change: a = {a}, b = {b}")

# Strings are also immutable
s1 = "hello"
s2 = s1
s2 = "world"  # Doesn't affect s1
print(f"s1 = {s1}, s2 = {s2}")

该示例显示修改不可变类型会创建新对象,而原始对象保持不变。输出将是

$ python main.py
Original: a = 10, b = 10
After change: a = 10, b = 20
s1 = hello, s2 = world

可变类型示例

赋值时,可变类型会共享引用,因此一个变量的更改会影响到指向同一对象的所有引用。

main.py
vals1 = [1, 2, 3]
vals2 = vals1  # Both reference the same list

print(f"Original: vals1 = {vals1}, vals2 = {vals2}")
vals2[0] = 99  # Modifies the shared list
print(f"After change: vals1 = {vals1}, vals2 = {vals2}")

# Dictionaries behave the same way
dict1 = {'a': 1}
dict2 = dict1
dict2['a'] = 100  # Affects both references
print(f"dict1 = {dict1}, dict2 = {dict2}")

这表明可变类型共享引用。输出显示一个变量的更改会影响另一个变量

$ python main.py
Original: vals1 = [1, 2, 3], vals2 = [1, 2, 3]
After change: vals1 = [99, 2, 3], vals2 = [99, 2, 3]
dict1 = {'a': 100}, dict2 = {'a': 100}

创建真正的副本

要创建可变对象的独立副本,请使用 copy 模块或特定于序列的方法

main.py
import copy

# Shallow copy example
original = [1, [2, 3], 4]
shallow_copy = copy.copy(original)

shallow_copy[0] = 10  # Doesn't affect original
shallow_copy[1][0] = 20  # Affects nested object in original

print(f"Original: {original}")
print(f"Shallow copy: {shallow_copy}")

# Deep copy example
deep_copy = copy.deepcopy(original)
deep_copy[1][0] = 30  # Doesn't affect original

print(f"Original: {original}")
print(f"Deep copy: {deep_copy}")

浅拷贝会创建新的容器,但共享对嵌套对象的引用。深拷贝会递归地复制所有对象。输出演示了区别

$ python main.py
Original: [1, [20, 3], 4]
Shallow copy: [10, [20, 3], 4]
Original: [1, [20, 3], 4]
Deep copy: [1, [30, 3], 4]

参数传递

Python 总是按对象引用传递参数。对于不可变对象,这表现得像按值传递,因为它们不能被修改。对于可变对象,函数可以修改原始对象。

main.py
def modify_list(lst):
    lst.append(4)  # Modifies the original list
    
def modify_number(num):
    num += 10  # Creates new object, doesn't modify original

numbers = [1, 2, 3]
modify_list(numbers)
print(f"List after modification: {numbers}")

x = 5
modify_number(x)
print(f"Number after modification: {x}")

列表是原地修改的,而数字保持不变

$ python main.py
List after modification: [1, 2, 3, 4]
Number after modification: 5

总结与最佳实践

理解 Python 的对象模型有助于防止在处理复杂数据结构时出现错误并编写高效的代码。

在本教程中,我们探讨了 Python 中可变类型和不可变类型之间的区别,重点关注它们如何影响变量赋值和参数传递。我们了解到,赋值时不可变类型会创建独立的副本,而可变类型则共享引用。这种理解对于编写正确高效的 Python 代码至关重要。我们还讨论了如何使用 copy 模块创建可变对象的真实副本,以及理解 Python 中参数传递的重要性。

来源

Python 标准类型

Python 复制模块

作者

我叫 Jan Bodnar,是一名热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 Python 教程