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

反转时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,

我试图理解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,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]
    是一个复杂的目标,它实际上通过
    矩阵进行赋值。但就我们的目的而言,是的,它们就像指向同一事物的两个独立指针变量。