我应该 return 一个通过引用传递和修改的列表吗?

Should I ever return a list that was passed by reference and modified?

我最近发现 python 中的列表自动通过引用传递(除非使用符号数组[:])。例如,这两个函数做同样的事情:

def foo(z):
    z.append(3)

def bar(z):
    z.append(3)
    return z

x = [1, 2]
y = [1, 2]
foo(x)
bar(y)
print(x, y)

以前,我总是返回我操作过的数组,因为我认为我必须这样做。现在,我明白这是多余的(而且可能效率低下),但似乎返回值通常是提高代码可读性的好习惯。我的问题是,执行这些方法中的任何一种是否有任何问题/最佳做法是什么?我缺少第三种选择吗?很抱歉,如果之前有人问过这个问题,但我找不到任何能真正回答我问题的东西。

这个答案是基于这样的假设,即关于是就地修改输入还是return复制的决定已经做出。

如您所述,是否 return 修改后的对象是一个见仁见智的问题,因为结果在功能上是等价的。一般来说, not return 就地修改的列表被认为是好的形式。根据 Zen of Python(第 2 项):

Explicit is better than implicit.

这在标准库中得到证实。列表方法在 SO 上为此臭名昭著:list.append, insert, extend, list.sort,等等

Numpy 也经常使用这种模式,因为它经常处理难以复制和 return 的大型数据集。一个常见的例子是数组方法 numpy.ndarray.sort, not to be confused with the top-level function numpy.sort,它 return 是一个新副本。

这个想法是 Python 思维方式的重要组成部分。以下是 Guido's email 的摘录,解释了原因和原因:

I find the chaining form a threat to readability; it requires that the reader must be intimately familiar with each of the methods. The second [unchained] form makes it clear that each of these calls acts on the same object, and so even if you don't know the class and its methods very well, you can understand that the second and third call are applied to x (and that all calls are made for their side-effects), and not to something else.

"best practice" 在技术上是根本不修改的东西:

def baz(z):
    return z + [3]

x = [1, 2]
y = baz(x)
print(x, y)

但一般来说,如果您将自己限制在 return 创建一个新对象或就地修改一个对象,而不是同时进行这两项操作,则情况会更清楚。

标准库中有一些示例可以就地修改对象和 return something(最重要的示例是 list.pop()),但是这是一个特殊情况,因为它没有 returning 被修改的对象

当然没有严格的应该,但是,一个函数should either do something, or return something.。因此,您最好要么在不 return 任何内容的情况下就地修改列表,要么 return 一个新列表,保持原始列表不变。

Note: the list is not exactly passed by reference. It's the value of the reference that is actually passed. Keep that in mind if you re-assign

Python 内置函数通常不会同时执行这两项操作,以避免混淆 function/method 是修改其参数还是 return 是一个新值。当就地修改时,不执行 return(使其隐式 return None)。例外情况是变异函数 return 不是被变异的对象(例如 dict.popdict.setdefault)。

遵循相同的模式通常是个好主意,以避免混淆。