反转时Python列表和矩阵的指针地址
我试图理解Python矩阵与Java/C风格的2D数组相比是如何实现的 具体而言,我面临的问题是: 给定一个矩阵(列表列表),我被要求将矩阵中的各个列表颠倒过来。我想出了以下代码:反转时Python列表和矩阵的指针地址,python,list,pointers,matrix,reverse,Python,List,Pointers,Matrix,Reverse,我试图理解Python矩阵与Java/C风格的2D数组相比是如何实现的 具体而言,我面临的问题是: 给定一个矩阵(列表列表),我被要求将矩阵中的各个列表颠倒过来。我想出了以下代码: 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,
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
)。因此变量矩阵
应该得到有效的更改。因为变量矩阵[i]是变量列表。因此,如果列表
被修改,那么矩阵也应该被修改
为了说明我前面的观点,提供了以下代码:
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"
如果矩阵[i]是列表
,则更改列表
意味着更改矩阵[i]
,更改矩阵[i]
意味着更改矩阵
如果我修改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
起作用。在第一次循环中,list
只是矩阵[0]
的一个名称
突变列出
名称的对象(如代码2中)显然会突变矩阵[0]
名称的对象,因为它们命名的是同一个对象
但是仅仅将列表
重新绑定到不同的对象,就像在代码1中一样,不会以任何方式改变矩阵[0]
。如果你仔细想想,这是有道理的。毕竟,在下一次循环中,list
将反弹到matrix[1]
,您肯定不希望这会改变matrix[0]
中的内容,对吗
在C语言中(如果您使用的是普通的CPython实现,这是事实),作为同一对象的名称意味着作为指向同一对象的指针。如果列表
是一个列表*
,则分配给列表
不会对*列表
中的任何内容执行任何操作
那么,为什么代码4可以工作呢?好的,在代码4中,您仍然没有改变列表,但是您正在重新绑定矩阵[0]
,而不是列表
,当然,重新绑定矩阵[0]
我猜,尽管谈到“java/c”,你真的用C++来思考。在C++中,=/Cord>是一个运算符,可以重载。另外,您不仅有指针,还有引用,它们可以神奇地工作,而不需要显式地取消引用它们。因此,如果list
是对列表对象的引用,而不是指针,list=
并没有将其更改为对另一个列表对象的引用,而是调用一个特殊方法,ListType::operator=
。这其实很奇怪。Java中没有这样的东西。或者C.比Python中更多
有关封面下发生的事情的更多详细信息:
如果您想用C语言来理解它,那么main(CPython)实现使用的实际C API可能会在这里说明问题
- 函数的局部变量只是指向Python对象的指针数组。矩阵
是局部变量[0]
,列表是局部变量[1]
,等等
*locals[0]
是一个PyListObject
struct,其中包含指向Python对象数组的指针。每个都指向另一个PyListObject
struct。但在这些内部列表的数组中是指向PyLongObject
structs的指针,它只包含数字
的循环比这复杂一点,但假设它只是在执行局部变量[1]=(*局部变量[0])元素[0]
,然后执行局部变量[1]=(*局部变量[0])。元素[1]
,等等
因此,分配给列表
只是改变局部变量[1]
,而不是*局部变量[1]
,因此它不会改变*局部变量[0]。元素[0]
但分配给*局部变量[0]。元素[0]
则是另一回事
调用reverse
方法也是如此。当您这样做时,self
只是作为指向同一对象的另一个指针结束,但它的实现在*self
上发生了变化
如果你在Java 2D数组中尝试,它也不会起作用。那么你的意思是说列表和矩阵[0]是不同的变量,它们在内存中占据不同的地址?@SounakBhattacharya不完全一样,但对于理解C语义并被Python搞糊涂的人来说,这是一个很好的类比。实际上,list
是一个在局部数组中转换为索引的名称,matrix[0]
是一个复杂的目标,它实际上通过矩阵进行赋值。但就我们的目的而言,是的,它们就像指向同一事物的两个独立指针变量。