是否可以在python中使用多个赋值来转置多维列表?

时间:2016-03-27 19:46:27

标签: python list multidimensional-array variable-assignment transpose

为了避免创建一个相同维度的新列表,我想知道多个赋值是否允许一个简单的转置函数来修改一个现有的数组。此函数用作具有state属性的类的方法,该属性是多维列表。像这样:

def transpose(self):
    for i in range(dim):
        for j in range(dim):
            self.state[i][j], self.state[j][i] = self.state[j][i], self.state[i][j]
    return self

虽然运行这个似乎让我回到同一个列表。我也有兴趣为什么这样的事情应该/不应该在理论上起作用。我已经研究了其他有效的转置列表方法(zip等),这更像是一个特定于任务的问题。谢谢你的帮助。

3 个答案:

答案 0 :(得分:1)

您正在遍历每个元素,这意味着您将交换两次。

(i=0, j=1)的示例中,您将进行交换 (i=0, j=1)< - > (i=1, j=0)(i=1, j=0),你会互换 (i=1, j=0)< - > (i=0, j=1) 再次

将第二个循环更改为for j in range(i + 1, dim)以仅遍历右上半部分(三角形)。

答案 1 :(得分:0)

这确实有效,self.state引用的对象不会改变。如果您为列表使用中间对象,则可以重新分配self.state以指向它:

def transpose(self):
    result = [[None for column in row] for row in self.state]
    for i in range(len(self.state)):
        for j in range(len(self.state[i])):
            result[j][i], result[i][j] = self.state[i][j], self.state[j][i]
    self.state = result

答案 2 :(得分:0)

免责声明:我没有测试过上面的任何代码,所以如果有错误我会道歉。另外,我假设我们在这里讨论Python 3,但我认为这段代码在Python 2中仍然可以正常工作。

没有额外的函数调用。

例如,假设您有一个Matrix类,如下所示:

from copy import copy

class Matrix:
    def __init__(self, rows):
        self._rows = rows

    @property
    def rows(self):
        return list(map(copy, self._rows))

    @rows.setter
    def rows(self, rows):
        self._rows = list(map(copy, rows))

    def cols(self):
        return list(map(list, zip(*self._rows)))

    @cols.setter
    def cols(self, cols):
        self._rows = list(map(list, zip(cols)))

    def transpose(self):
        self.cols = self._rows

转置功能甚至不需要使用列表解包。但是这仍然使用zip,你提到你正在考虑取消它。

如果要使用列表解包,则必须知道将解压缩的值数。因此,如果您支持可变维度,那么这是不行的。否则,您必须对更改进行硬编码。

但还有另一个问题。由于转置会改变非方形矩阵的尺寸,因此您无法进行元素交换。在这种情况下会发生IndexError。你必须进行行或列式交换。为此,您必须在某个时刻复制列或行(如上面的代码所示 - 请注意,不需要使用getter副本)。

你可以使用一种技术。您可以将多维列表存储为一维列表,只需使用数学来确定行(或列)的开始位置。例如,您可以执行以下操作:

class Matrix:
    def __init__(self, values, number_of_columns, number_of_rows):
        self._values = values
        self._number_of_columns = number_of_columns
        self._number_of_rows = number_of_rows

    @property
    def rows(self):
        return [self._values[i * self._number_of_rows:(i + 1) * self._number_of_rows] for i in range(self._number_of_rows)]

    @property
    def cols(self):
        return [self._values[i::self._number_of_columns] for i in range(self._number_of_columns)]

    def transpose(self):
        for i in range(self._number_of_rows):
            self._values[i * self._number_of_rows:(i + 1) * self._number_of_rows], self._values[i::self._number_of_rows] = self._values[i::self._number_of_columns], self._values[i * self._number_of_columns:(i + 1) * self._number_of_columns]
        self._number_of_columns, self._number_of_rows = self._number_of_rows, number_of_columns

虽然第一次迭代将值交换到适当的位置,但在非方形矩阵中,某些值会丢失。例如,考虑构造为Matrix(list(range(6)), 3, 2)的矩阵。

每次传递如下:

  1. self._values == [0, 3, 1, 3, 2, 5]
  2. self._values == [0, 3, 3, 2, 2, 5]
  3. 请注意,它会将值放在第一遍的正确位置。但它会覆盖尚未移动的值。避免这种情况的唯一方法是制作副本。因此,可以更改transpose方法,使其工作如下:

        def transpose(self):
            new_values = []
            for i in range(self._number_of_rows):
                new_values.extend(self._values[i::self._number_of_columns])
            self._values = new_values
            self._number_of_columns, self._number_of_rows = self._number_of_rows, number_of_columns
    

    但这非常复杂且难以调试。所以故事的寓意就是使用self.state = list(zip(*self.state)) - 它很容易阅读,很难搞乱 - 或者你在transpose方法中已有的东西。

    乍一看,您的transpose方法似乎进行了双重换位。当然,矩阵M的转置的转置与M相同。你需要使内环更小的天花板:

    def transpose(self):
        for i in range(dim):
            for j in range(i):
                self.state[i][j], self.state[j][i] = self.state[j][i], self.state[i][j]
        return self