据我所知,python将参数作为参考传递。我有以下代码
def func(arr):
print(arr)
if arr == [] :
return
for i in range(len(arr)):
arr[i] *= 2
func(arr[1:])
r = [1,1,1,1]
func(r)
print(r)
我希望输出为[2,4,8,16]。
为什么它输出[2,2,2,2]就像引用仅适用于一级递归一样?
也许'arr [1:]'总是创建一个新对象?如果是这样,有什么办法可以使arr [1:]工作?
答案 0 :(得分:2)
您问了几个不同的问题,让我们一个一个地解决它们。
据我所知,python将参数作为参考传递。
python如何传递参数的正确术语是"pass py assignment"。这意味着函数内部的参数的行为类似于如果直接给它们分配了=
符号时的行为。 (因此,变异将反映在对对象的所有引用中。到目前为止,一切都很好)
侧注(如果造成混淆,请跳过):就所有意图和目的而言,“通过赋值传递”的区别很重要,因为它抽象化了按值传递与按引用传递的概念,而这些概念并未直接在python中公开。如果您想了解底层机制的工作原理,它实际上是一个值传递,但是每个值本身都是对对象的引用(相当于对象中的第一级指针) C说话)。我们可以看到为什么一开始不用担心这种特定的抽象更容易和重要的原因,并使用“按分配传递”作为更直观的解释。
下一步,
也许'arr [1:]'总是创建一个新对象吗?
正确的切片总是会创建列表的浅表副本。 docs
如果是这种情况,有什么办法可以使arr [1:]工作?
不是直接的,但是我们可以使用索引来构建一个可行的解决方案,并为您提供所需的输出。只需在执行递归时跟踪起始索引,然后在继续递归时递增索引即可。
def func(arr, start=0):
print(arr)
if arr[start:] == [] :
return
for i in range(start, len(arr)):
arr[i] *= 2
func(arr, start + 1)
r = [1,1,1,1]
func(r)
print(r)
输出:
[1, 1, 1, 1]
[2, 2, 2, 2]
[2, 4, 4, 4]
[2, 4, 8, 8]
[2, 4, 8, 16]
[2, 4, 8, 16]
答案 1 :(得分:1)
您对切片arr[1:]
进行切片,结果将创建新列表。这就是为什么您得到这样的结果,以后我不建议您这样做,因为它是隐式的并且很难调试。使用函数时,请尝试返回新值,而不是通过引用更改
例如这样的
def multiplier(arr):
return [
value * (2 ** idx)
for idx, value in enumerate(arr, start=1)
]
result = multiplier([1, 1, 1, 1])
print(result) # [2, 4, 8, 16]
答案 2 :(得分:0)
切片列表将创建一个新对象(正如您所推测的),这将解释为什么在第一次调用后原始列表没有更新的原因。
答案 3 :(得分:0)
是的,arr[1:]
创建一个新对象。您可以传递索引以指示起始索引。
def func(arr, start_idx):
print(arr)
if arr == [] :
return
for i in range(start_idx, len(arr)):
arr[i] *= 2
func(arr, start_idx + 1)
r = [1,1,1,1]
func(r, 0)
print(r)
答案 4 :(得分:0)
您可以使用它。变量s表示数组的开始,变量e表示数组的结束。
def func(arr,s,e):
print(arr) #comment this line if u dont want the output steps
if s>=e:
return
for i in range(s,e):
arr[i] *= 2
func(arr,s+1,e)
r = [1,1,1,1]
func(r,0,len(r))
print(r)