倒转时Python列表和矩阵的指针地址

时间:2018-03-07 03:03:41

标签: python list pointers matrix reverse

我试图理解与Java / C风格的2D数组相比如何实现Python矩阵。

具体来说,我面临的问题是:

给定一个矩阵(列表列表),我被要求在原地反转矩阵中的各个列表。我想出了以下代码:

CODE 1
------

def flip(matrix):
    for list in matrix:
        list=list[::-1]

matrix=[[1,0,0],[0,0,1]]
flip(matrix)
print(matrix) # Outputs "[[1,0,0],[0,0,1]]" i.e. does not reverse

如果我稍微修改一下代码,

CODE 2
------

def flip(matrix):
    for list in matrix:
        list.reverse()

matrix=[[1,0,0],[0,0,1]]
flip(matrix)
print(matrix) # Outputs "[[0,0,1],[1,0,0]]" i.e. works correctly this time

我知道list.reverse()执行就地操作,list[::-1]创建浅层副本。但是在CODE 1中,我只将浅拷贝的地址分配给同一个变量(list)。因此变量matrix应该有效地改变。因为变量矩阵[i] is变量列表。因此,如果list被修改,matrix也应如此。

为了说明我之前的观点,提供了以下代码:

CODE 3
------

def test(matrix):
    for i, list in enumerate(matrix):
        print(matrix[i] is list)

matrix=[[1,0,0],[0,0,1]]
test(matrix) # Outputs "True True"

如果matrix[i] is list,则更改list表示更改matrix[i],更改matrix[i]表示更改matrix

如果我修改CODE 1,而不是为list分配新创建的反向列表的地址,则为matrix[i]分配该地址,然后令人惊讶的是它有效!

CODE 4
------

def flip(matrix):
    for i, list in enumerate(matrix):
        # Instead of  list=list[::-1]
        matrix[i]=list[::-1] 

matrix=[[1,0,0],[0,0,1]]
flip(matrix)
print(matrix) # Correctly Outputs [[0,0,1], [1,0,0]]

我想解释为什么CODE 1不起作用以及为什么CODE 4有效。

1 个答案:

答案 0 :(得分:2)

第一次循环播放时,list只是matrix[0]的名称。

list名称的对象进行变换,如在CODE 2中那样,显然会改变matrix[0]命名的对象,因为它们正在命名同一个对象。

但是,只需将list重新绑定到某个不同的对象(如代码1中),就不会以任何方式更改matrix[0]。如果你考虑一下,这是有道理的。毕竟,下一次循环,list将会反弹到matrix[1],你肯定不希望改变matrix[0]中的内容,对吗?

在C语言中(如果您使用正常的CPython实现,这确实是真的),作为同一对象的名称意味着指向同一个对象。如果listList *,则分配给list并不会对*list中的任何内容执行任何操作。

那么,为什么CODE 4有效呢?好吧,在代码4中,您仍然没有改变列表 - 但是您重新绑定matrix[0]而不是list,当然重新绑定matrix[0]。< / p>

我猜测,尽管在谈论&#34; Java / C&#34;,你真的在​​用C ++术语思考。在C ++中,=是一个可以重载的运算符。另外,你不仅仅有指针,而是引用,这些神奇的工作无需明确地取消引用它们。因此,如果list是对列表对象的引用,而不是指针,list =不会将其更改为对另一个列表对象的引用,则它会调用特殊方法,ListType::operator=。这真的很奇怪。在Java中没有那样的东西。或者C.比Python更多。

有关封底内容的详细信息:

如果你想用C语言来思考它,那么main(CPython)实现所使用的实际C API可能会在这里说清楚。

  • 你的函数的本地只是一个指向Python对象的数组。 matrixlocals[0]listlocals[1]等。
  • *locals[0]中的内容是PyListObject结构,其中包含指向Python对象数组的指针。每个都指向另一个PyListObject结构。但是在那些内心清单中&#39;数组是指向PyLongObject结构的指针,只保存数字。
  • for循环比这更复杂,但假装它只是locals[1] = (*locals[0]).elements[0],然后是locals[1] = (*locals[0]).elements[1]等。
  • 因此,分配到list只是更改locals[1],而不是*locals[1],因此它不会更改*locals[0].elements[0]
  • 但是分配给*locals[0].elements[0]是另一回事。
  • 所以调用reverse方法。当你这样做时,self只是另一个指向同一个对象的指针,但它的实现会改变*self上的内容。