提问人:Michael Skarn 提问时间:8/6/2019 更新时间:8/6/2019 访问量:1875
我是否应该返回通过引用传递和修改的列表?
Should I ever return a list that was passed by reference and modified?
问:
我最近发现 python 中的列表是通过引用自动传递的(除非使用表示法 array[:])。例如,这两个函数执行相同的操作:
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 的禅宗(项目 #2):
显式比隐式更好。
这在标准库中得到了证实。List 方法在 SO 上是臭名昭著的:list.append、insert
、extend
、list.sort
等。
Numpy 也经常使用这种模式,因为它经常处理复制和返回不切实际的大型数据集。一个常见的例子是数组方法 numpy.ndarray.sort,不要与顶级函数 numpy.sort
混淆,后者返回一个新副本。
这个想法在很大程度上是 Python 思维方式的一部分。以下是 Guido 电子邮件的摘录,解释了原因和原因:
我发现链接形式对可读性构成威胁;它要求读者必须非常熟悉每种方法。第二种 [unchained] 形式清楚地表明,这些调用中的每一个都作用于同一个对象,因此,即使你不太了解该类及其方法,您也可以理解第二个和第三个调用都应用于 x(并且所有调用都是为了它们的副作用),而不是其他东西。
评论
从技术上讲,“最佳实践”是完全不修改它:
def baz(z):
return z + [3]
x = [1, 2]
y = baz(x)
print(x, y)
但一般来说,如果你限制自己返回一个新对象或就地修改一个对象,而不是同时修改一个对象,而不是同时修改一个对象,那就更清楚了。
标准库中有一些示例既可以就地修改对象,也可以返回某些内容(最重要的示例是 ),但这是一种特殊情况,因为它不会返回被修改的对象。list.pop()
评论
str
x[0] = 3
append
当然,没有严格的应该,但是,函数应该要么做某事,要么返回某事。因此,您最好要么在不返回任何内容的情况下修改列表,要么返回一个新列表,保持原始列表不变。
注意:该列表并非完全通过引用传递。它是实际传递的引用的值。如果您重新分配,请记住这一点
Python 内置函数通常不会同时执行这两项操作,以避免混淆函数/方法是就地修改其参数还是返回新值。就地修改时,不执行 (使其隐式返回 )。例外情况是,突变函数返回的对象不是被变异的对象(例如,)。return
None
dict.pop
dict.setdefault
遵循相同的模式通常是个好主意,以避免混淆。
评论
list
list
list