如何通过引用传递列表?

Posted

技术标签:

【中文标题】如何通过引用传递列表?【英文标题】:How to pass a list by reference? 【发布时间】:2018-06-29 23:46:39 【问题描述】:

我正在尝试实现一个函数“添加”,它将列表L1L2 组合成L3

def add(L1,L2,L3):
    L3 = L1 + L2

L3 = []
add([1],[0],L3)
print L3

上面的代码生成一个空列表而不是[1,0] - 这意味着L3 不是通过引用传递的。

如何通过引用传递L3

【问题讨论】:

不要;只需返回组合列表。 L3 = add([1], [0]). 是的,python支持引用传递语义 【参考方案1】:

如果您想就地进行更改,则需要执行修改列表的操作就地。这意味着任何list 实例方法。在这种特殊情况下,您可以查看list.extend

def add(L1, L2, L3):
    L3.extend(L1 + L2)   # creates a new list object and copies it into L3

由于这会创建一个副本(这可能效率低下),您可以改为使用两个 extend 调用来代替它 -

def add(L1, L2, L3):
    L3.extend(L1)        
    L3.extend(L2)

调用L3.extend 会修改原始列表。使用您当前的代码,L1 + L2 会创建一个新列表,并将 L3 重新分配给该对象(并且未触及 L3 所指的原始对象)。


如果您想将此推广到任意数量的列表,我建议您看看使用可变参数:

from itertools import chain

def add(L3, *L):
    L3.extend(chain.from_iterable(L))

这是一个如何工作的例子:

>>> L3 = []
>>> add(L3, [1], [2], [3])
>>> L3
[1, 2, 3]

作为一种良好做法,您应该

定义一个函数,该函数不修改原始结构,但返回一个可以从调用者分配的新值,或者 定义一个修改结构的函数,并且不返回任何内容。

另一个用户注释:L3[:] = ...替换 L3 的所有以前的内容与来自 L1 + L2 的项目。另一方面,L3.extend(...) 不会破坏之前在L3 中的内容,而是通过传递的内容扩展。我建议评估您的用例并使用更适合这种情况的方法。

但是,为了适应这种情况,您可以使用 __setitem__ 的就地分配来构建接受的答案的方法:

def add(L3, *L):
    L3[:] = chain.from_iterable(L)

它的工作方式相同,并且对于许多列表应该非常有效。

【讨论】:

【参考方案2】:

列表已经通过引用传递,因为 所有 Python 名称都是引用,而列表对象是可变的。使用切片赋值而不是普通赋值。

def add(L1, L2, L3):
    L3[:] = L1 + L2

但是,这不是编写函数的好方法。您应该简单地返回组合列表。

def add(L1, L2):
    return L1 + L2

L3 = add(L1, L2)

【讨论】:

"使用切片分配而不是普通分配。"那么为什么正常分配不起作用呢? 因为L1 + L2 创建了一个分配给L3 的新列表对象,而不是替换L3 已经引用的列表的元素。在许多情况下,这可能无关紧要,但 OP 专门询问他们希望更新现有列表的场景。 所以 L3(对列表 L3 的引用)是按值传递的,而不是按引用传递的,这就是为什么您不能直接更改函数内部的 L3(引用),而必须这样做“切片分配”(因此不必更改 L3)? :P C++ 在这方面有多优雅。 函数与它关系不大。 L3 = ... 从不L3 当前所指的任何内容有任何关系。【参考方案3】:

您不能在 Python 中通过引用显式传递变量,但可以更新现有对象的属性。

因此,您可以做的是更新 L3 上的值,所有这些值都在您的情况下,所以您可以这样做:

def add(L1 ,L2, L3):
    L3[:] = L1 + L2

L3 = []

add([1], [0], L3)

print L3

【讨论】:

【参考方案4】:

您可以通过以下方式实现:

L3[:] = L1 + L2

测试代码:

def add(L1, L2, L3):
    L3[:] = L1 + L2

L3 = []
add([1], [0], L3)
print(L3)

结果:

[1, 0]

【讨论】:

我能感同身受的东西!

以上是关于如何通过引用传递列表?的主要内容,如果未能解决你的问题,请参考以下文章

c# 中是不是默认通过引用传递数组或列表?

我应该返回一个通过引用传递并修改的列表吗?

如何在不传递引用的情况下在 Python 中使用 SyncManager 跨进程共享列表

c# - 我应该使用“ref”通过引用方法来传递集合(例如列表)吗?

通过引用传递 - 左值和向量

传递引用参数与包装器类型的长列表