编程后端开发工程师

在 Python 中,按引用和按值传递参数是什么? Python 如何实现这一机制,以及在设计函数时区分它们的重要性是什么?

用 Hintsage AI 助手通过面试

答案。

理解 Python 如何将参数传递给函数,对于防止数据意外更改和正确设计代码极为重要。

问题的历史

在传统编程语言中,如 C 或 Java,使用按值传递 (copy by value) 或按引用传递 (copy by reference)。但 Python 使用的是不同的模型——按对象引用调用 (有时称为 "按共享调用")。

问题

许多开发人员错误地认为 Python 总是按引用或按值传递参数。这必然会导致可变对象在调用代码中意外被修改。

解决方案

在 Python 中,函数参数的值是对传递给函数的对象的引用。这意味着:

  • 如果对象是 可变的 (mutable: list, dict, set…) — 在函数内部可以修改它,且这会反映到外部。
  • 如果对象是 不可变的 (immutable: int, str, tuple, frozenset),在函数内部尝试修改它将导致创建一个新对象,而不影响外部。

示例:

# list - 可变的 (mutable) def add_item(lst): lst.append(42) my_list = [1, 2, 3] add_item(my_list) print(my_list) # [1, 2, 3, 42] # int - 不可变的 (immutable) def add_num(x): x = x + 1 num = 10 add_num(num) print(num) # 10

关键特征:

  • 可变对象可以在函数内部被修改,这些变化在外部是可见的。
  • 不可变对象不会被函数影响——只会创建新的对象。
  • Python 从不自动复制参数,即使是可变结构始终是 "按引用" 传递的。

误导性问题。

在 Python 中,参数总是按引用传递吗?

不,在 Python 中,传递的是对对象的引用,而对象的行为取决于它是否是可变的。不可变对象在任何修改下都会创建新对象。

可以在函数中重新赋值可变参数,以影响外部对象吗?

不可以。如果您在函数内部给参数重新赋值,外部对象不会改变——您只是更改了局部引用。

示例:

def reassign_list(lst): lst = [99, 100] my_list = [1, 2, 3] reassign_list(my_list) print(my_list) # [1, 2, 3]

为什么默认接受 list 的函数在多次调用时可能表现奇怪?

因为默认值在函数定义时只创建一次,如果更改它(例如,添加元素),它在后续调用中会发生变化。

def add_element(x, cache=[]): cache.append(x) return cache print(add_element(1)) # [1] print(add_element(2)) # [1, 2]

常见错误和反模式

  • 使用可变的默认参数(如最后一个示例中)。
  • 期望函数不会更改传递的 list 或 dict,虽然它会修改。
  • 混淆在处理可变和不可变对象时的函数效果。

生活中的例子

负面案例

程序员将列表传递给函数,并期望他们的原始列表不会改变,但函数添加了一个元素。

优点:

  • 运行快速,无需复制数据。

缺点:

  • 意外的副作用,代码中的错误,如果有人不知道参数的修改。

正面案例

程序员在函数内部明确复制列表,如果需要返回某些内容,但不想更改原始:

def process_data(data): data = data.copy() # 或 list(data) # 安全处理副本 data.append('报告') return data

优点:

  • 没有不必要的副作用,原始数据得到保护。

缺点:

  • 对于大型对象,复制会导致内存/时间的开销。