我试图理解与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
有效。
答案 0 :(得分:2)
第一次循环播放时,list
只是matrix[0]
的名称。
对list
名称的对象进行变换,如在CODE 2中那样,显然会改变matrix[0]
命名的对象,因为它们正在命名同一个对象。
但是,只需将list
重新绑定到某个不同的对象(如代码1中),就不会以任何方式更改matrix[0]
。如果你考虑一下,这是有道理的。毕竟,下一次循环,list
将会反弹到matrix[1]
,你肯定不希望改变matrix[0]
中的内容,对吗?
在C语言中(如果您使用正常的CPython实现,这确实是真的),作为同一对象的名称意味着指向同一个对象。如果list
是List *
,则分配给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可能会在这里说清楚。
matrix
为locals[0]
,list
为locals[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
上的内容。