Python 我是否应该返回通过引用传递并修改的列表?

Python 我是否应该返回通过引用传递并修改的列表?,python,pass-by-reference,pass-by-value,Python,Pass By Reference,Pass By Value,我最近发现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) 在此之前,我总是返回我操纵的数组,因为我认为我必须这样做。现在,我明白这是多余的(也许效率低下),但返回值似乎通常是代码可读性的良好实践。我的问题是,采

我最近发现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)

在此之前,我总是返回我操纵的数组,因为我认为我必须这样做。现在,我明白这是多余的(也许效率低下),但返回值似乎通常是代码可读性的良好实践。我的问题是,采用这两种方法中的任何一种都有问题吗?最佳做法是什么?有没有第三种选择我错过了?如果以前有人问过我,我很抱歉,但我找不到任何真正能回答我问题的答案。

这个答案的前提是,已经做出了修改输入或返回副本的决定

正如您所指出的,是否返回修改过的对象是一个意见问题,因为结果在功能上是等价的。通常,不返回已修改的列表被认为是一种良好的形式。根据第(2)项:

显式比隐式好

这在标准图书馆中得到了证实。列表方法因此而臭名昭著,例如:,等等

Numpy也经常使用这种模式,因为它经常处理无法复制和返回的大型数据集。一个常见的例子是数组方法,不要与返回新副本的顶级函数混淆

这个想法在很大程度上是Python思维方式的一部分。以下是一段摘录,解释了原因:

我发现链接形式对可读性构成威胁;它要求读者必须非常熟悉每种方法。第二个[unchecked]表单清楚地表明,这些调用中的每一个都作用于同一个对象,因此,即使您对类及其方法不太了解,您也可以理解第二个和第三个调用应用于x(所有调用都是针对其副作用进行的),而不是其他对象

“最佳实践”从技术上讲是根本不修改:

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

x = [1, 2]
y = baz(x)
print(x, y)
但一般来说,如果您限制自己返回一个新对象或在适当的位置修改一个对象,但不能同时返回和修改这两个对象,则情况会更清楚

标准库中有一些示例既修改了对象,又返回了一些内容(最重要的示例是
list.pop()
),但这是一种特殊情况,因为它不返回已修改的对象。

当然,没有严格的定义,应该是函数。因此,您最好在不返回任何内容的情况下修改列表,或者返回一个新列表,保持原始列表不变

注意:列表不是通过引用传递的。实际传递的是引用的值。如果重新分配,请记住这一点


通常,Python内置不会同时执行这两项操作,以避免混淆函数/方法是就地修改其参数还是返回新值。就地修改时,不执行任何
返回
(使其隐式返回
)。例外情况是,变异函数返回的不是变异对象(例如,
dict.pop
dict.setdefault


一般来说,遵循相同的模式是一个好主意,以避免混淆。

“自动通过引用传递”从技术上讲,这是不正确的。Python通过引用的副本传递信息。这些引用可能是对可变或不可变对象的引用,而这正是产生混淆的原因。参见@DeepSpace。“通过引用”和“通过值”并不是唯一可用的选项。“pythonic”方法——如果您正在编写一个更改
列表的函数——
——是不返回任何内容(除非您有很好的理由这样做)。相反,在函数docstring中特别指出该函数修改
列表
。此外,python既不通过值也不通过引用传递对象,您需要这样做。这只是一个玩具示例,还是您实际上正在编写一个修改
列表的函数?如果是这样,你为什么认为你需要这样做?@RickTeachey。这只是一个玩具示例,但我确实编写了修改列表的函数。据我所知,一个人可以选择1。修改函数中的列表(在阅读此处的答案后,也不应返回可读性),或2。创建列表的副本并返回该副本。我听说前者是一种更便宜的操作,这使我认为在大多数情况下它会更好。这有助于对抗臭名昭著的方法链接反模式:)另一方面,返回列表的修改副本也是一种很好的形式。对于给定函数,哪种形式更好取决于函数和上下文。@Serge方法链接本身不是一种反模式,但它有时间和地点,不应在其他地方(如添加到列表)误用。@jirassimok。OP具体描述了现场情况。如果你处理的是复印件,你几乎必须归还复印件。我本可以用更好的措辞来评论这一点。我基本上是想澄清,有时候你想要修改副本,而不是修改输入,我不想将其作为单独的答案添加,因为它并不能真正回答问题。但是修改原始列表不是比复制更快吗?或者差异可以忽略不计?@MichaelSkarn是的,内存效率更高,但出于某种原因,人们可能仍然想要原始列表(你会注意到,
str
类的几乎每个方法都返回一个新的、更改过的实例,而不是修改字符串本身)。@MichaelSkarn。这取决于列表的大小以及修改的内容<代码>x[0]=3
在一个包含10**6个元素的列表上,可能比创建一个新元素更便宜。呼叫