如何通过引用传递列表?
Posted
技术标签:
【中文标题】如何通过引用传递列表?【英文标题】:How to pass a list by reference? 【发布时间】:2018-06-29 23:46:39 【问题描述】:我正在尝试实现一个函数“添加”,它将列表L1
与L2
组合成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]
【讨论】:
我能感同身受的东西!以上是关于如何通过引用传递列表?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不传递引用的情况下在 Python 中使用 SyncManager 跨进程共享列表