【问题标题】:Python: Unexpected behavior when function argument is replicated list of listsPython:复制函数参数时的意外行为
【发布时间】:2020-10-24 16:31:56
【问题描述】:

在被调用时作为可变对象的列表正在传递引用(如果我的理解是正确的,它本质上是传递 id)。在下面的代码中,我分配了一个列表 A 的列表。我将它称为修改 M 的名为 modify(M) 的函数。正如预期的那样,A 和 M 具有相同的 id,并且修改 M 也会修改 A。到目前为止一切都很好。

但是,我随后将 A 重新分配为原始值(如预期的那样更改其 id)。然后我调用 modify(A*1),即将复制的 A 作为参数传递。正如预期的那样,现在 A 和 M 具有不同的 id,而 M 是 A 返回 False。 但是,改变 M 仍然会改变 A,这与我的预期相反。这是为什么呢?

注意:调用 modify(A[:]) 也会产生同样的意外(我)行为。

我知道答案与我有一个列表有关。 (当我仅使用一级列表尝试此操作时,我得到了预期的行为。)

由于我是 Python 编码的相对初学者,我非常感谢初学者可能理解的答案。

代码如下:

def modify(M):
    print(M is A)
    print('id of M', id(M))
    print('id of A', id(A))
    print('Original list of lists before change in M =', A)
    M[0][0]=M[1][1] #some modification of M
    print('Original list of lists after change in M =', A)
    print(M is A)
    print('id of M', id(M))
    print('id of A', id(A))

A=[[1,2],[3,42]]
modify(A)    #Argument is the list 
print("\n","*****What happens below is driving me crazy!*****","\n")
A=[[1,2],[3,42]]
modify(A*1)  #Argument is replicated list of lists
#modify(A[:]) has the same behavior

编辑:我刚刚发现并没有为我回答这个问题,但可能为比我更擅长 Python 的人回答了这个问题。

它要求您导入副本。

  1. modify(A*1) 替换为modify(copy.copy(A))。观察到完全相同的奇怪行为。换句话说,id 变了,而 M 变了,A 也变了。
  2. modify(A*1) 替换为modify(deepcopy.copy(A))。我所期望的会发生。换句话说,id 变了,M 变了,A 也不会变。

【问题讨论】:

  • 如果你使用不可变的数据结构作为参数(比如元组)也会发生同样的事情
  • 好问题。当然,如果将 A 分配为元组的元组,则代码会崩溃。但是如果将 A 分配为列表 A=([1,2],[3,42]) 的元组,则 A 在 M 更改时再次更改,但现在 A 和 M 具有相同的 id
  • 是的,我的意思是一个元组的元组
  • 对于一个元组的元组,您尝试修改一个元组并且您得到 TypeError: File "L:__Transfer\test0008.py", line 13, in modify M[0][0]= M[1][1] #M TypeError的一些修改:'tuple'对象不支持项赋值
  • 是的,你必须复制对象

标签: python list function arguments


【解决方案1】:

好的,我想出了我的问题的答案:

然后我调用 modify(A*1),即将复制的 A 作为参数传递。正如预期的那样,现在 A 和 M 具有不同的 id,而 M 是 A 返回 False。 但是,更改 M 仍然会更改 A,这与我的预期相反。这是为什么呢?

回答: 列表列表有一个 id。但二级列表的项目也有自己的 id。我编辑了代码以打印二级项目 M[0][0] 和 M[1][1] 的 id:

def modify(M):
    print(M is A)
    print('id of M and list items', id(M), id(M[0][0]), id(M[1][1]))
    print('id of A and list items', id(A), id(A[0][0]), id(A[1][1]))
    print('Original list of lists before change in M =', A)
    M[0][0]=M[1][1] #some modification of M`
    print('Original list of lists after change in M =', A)
    print(M is A)
    print('id of M and list items', id(M), id(M[0][0]), id(M[1][1]))
    print('id of A and list items', id(A), id(A[0][0]), id(A[1][1]))

A=[[1,2],[3,42]]
modify(A)    #Argument is the list 
print("\n","*****What happens below is driving me crazy!*****","\n")
A=[[1,2],[3,42]]
modify(A*1)  #Argument is replicated list of lists
#modify(A[:]) has the same behavior`

这是打印输出:

正确
M 的 id 和列表项 2436965208832 140725589391136 140725589392448
A 的 ID 和列表项 2436965208832 140725589391136 140725589392448
M = [[1, 2], [3, 42]] 更改前的原始列表列表
M = [[42, 2], [3, 42]] 更改后的原始列表列表
真的
M 的 id 和列表项 2436965208832 140725589392448 140725589392448
A 的 id 和列表项 2436965208832 140725589392448 140725589392448

下面发生的事情快把我逼疯了!

错误
M 的 ID 和列表项 2436965208832 140725589391136 140725589392448
A 的 id 和列表项 2436956205440 140725589391136 140725589392448

M = [[1, 2], [3, 42]] 更改前的原始列表列表
M = [[42, 2], [3, 42]] 更改后的原始列表列表
假的
M 的 id 和列表项 2436965208832 140725589392448 140725589392448
A 的 id 和列表项 2436956205440 140725589392448 140725589392448

第一次调用 modify(A) 如预期的那样产生了一个 M,它不仅具有与 A 相同的 id,而且它的所有项目都具有与 A 中的对应项相同的 id。但是,第二次调用 modify(A*1) 产生一个 M id 与 A 不同,但项目 M[0][0] 和 M[1][1] 的 id 与 A[0][0] 和 A[1][ 的 id 保持相同1],分别。这就是为什么为 M[0][0] 分配一个新值会为 A[0][0] 分配相同的值。这就是观察到奇怪行为的原因。

经验教训:具有不同 id 的两个列表可能具有共享内存地址的组件。因此,将list1 is list2 解析为False 并不能保证list1 的组件更改不会更改list2 的相应组件。由于某种原因,replicate *1 命令会更改列表的 ID,但不会更改二级项目的 ID。

【讨论】:

    猜你喜欢
    • 2013-12-17
    • 1970-01-01
    • 2020-08-12
    • 1970-01-01
    • 2015-09-30
    • 2014-08-27
    • 2020-07-04
    • 2020-04-10
    • 1970-01-01
    相关资源
    最近更新 更多