简体   繁体   English

是否可以在python中使用多重分配来转置多维列表?

[英]Is it possible to use multiple assignment in python to transpose a multidimensional list?

In order to avoid creating a new list of equal dimension, I'm wondering if multiple assignment could allow for an easy transpose function that modifies an exiting array. 为了避免创建一个等维的新列表,我想知道多重分配是否可以允许一个简单的转置函数来修改现有数组。 This function is meant to act as a method for a class with a state property that is a multidimensional list. 此功能旨在充当state属性为多维列表的类的方法。 Something like this: 像这样:

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

Though running this seems to give me back the same list. 尽管运行此命令似乎可以使我返回相同的列表。 I'm also interested in why something like this should/shouldn't work theoretically. 我也对为什么这样的事情在理论上应该/应该不起作用感兴趣。 I've already looked into other efficient means of transposing lists ( zip , etc.), this is more of a task specific question. 我已经研究过其他有效的列表转换方法( zip等),这更多是针对特定任务的问题。 Thanks for your help. 谢谢你的帮助。

You are traverseing every element which means you will swap twice. 您遍历每个元素,这意味着您将交换两次。

In example at (i=0, j=1) you will swap (i=0, j=1) <-> (i=1, j=0) and at (i=1, j=0) you will swap (i=1, j=0) <-> (i=0, j=1) again . (i=0, j=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) 再次 (i=0, j=1)

Change the second loop to for j in range(i + 1, dim) to traverse only the upper right half (triangle). 将第二个循环更改for j in range(i + 1, dim)以仅遍历右上半部分(三角形)。

This does work, the object referenced to by self.state just won't change. 这确实有效, self.state引用的对象不会改变。 If you use an intermediate object for the list, self.state may be reassigned to point at it: 如果将中间对象用于列表, 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

Disclaimer : I have not tested any of the code above, so I apologize if there is a mistake. 免责声明 :我没有测试上面的任何代码,因此如果有错误,我深表歉意。 Additionally, I made the assumption that we're talking about Python 3 here, but I think that this code should still work fine in Python 2. 另外,我假设我们在这里谈论的是Python 3,但是我认为这段代码在Python 2中仍然可以正常工作。


Not without an extra function call. 并非没有额外的函数调用。

For example, let's say you have a Matrix class as follows: 例如,假设您有一个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

The transpose function wouldn't even need to use list unpacking. 转置功能甚至不需要使用列表拆包。 But this still uses zip , which you mentioned you're considering doing away with. 但这仍然使用zip ,您曾提到您正在考虑废止它。

If you want to use list unpacking, you have to know how many values will be unpacked. 如果要使用列表拆包,则必须知道要拆包多少个值。 Therefore, if you are supporting variable dimensions, then it's a no-go. 因此,如果您支持可变尺寸,那是不行的。 Otherwise, you have to hardcode the changes. 否则,您必须对更改进行硬编码。

But there's another issue at hand. 但是,还有另一个问题。 Since transposing would change the dimensions of a non-square matrix, you can't do element-wise swapping. 由于转置会更改非正方形矩阵的尺寸,因此无法进行逐元素交换。 An IndexError would occur in such a situation. 在这种情况下会发生IndexError You'd have to do row- or column-wise swapping. 您必须进行行或列交换。 To do that, you'd have to make a copy of the columns or rows at some point (as shown in the code above—note that the getter copies aren't necessary). 为此,您必须在某个时刻制作列或行的副本(如上面的代码所示,请注意,不需要getter副本)。

There is one technique that you could use though. 不过,您可以使用一种技术。 You could store your multidimensional list as a one-dimensional list and just use math to determine where a row (or column) begins. 您可以将多维列表存储为一维列表,而只需使用数学确定行(或列)的起始位置。 For example, you could do the following: 例如,您可以执行以下操作:

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

Though the first iteration swaps values to their proper places, in a non-square matrix, some values are lost. 尽管第一次迭代将值交换到适当的位置,但在非平方矩阵中,某些值会丢失。 For example, consider a matrix constructed like Matrix(list(range(6)), 3, 2) . 例如,考虑构造为Matrix(list(range(6)), 3, 2)

Each pass as follows: 每次通过如下:

  1. self._values == [0, 3, 1, 3, 2, 5]
  2. self._values == [0, 3, 3, 2, 2, 5]

Notice that it puts the values in the correct places on the first pass. 请注意,它会在第一次通过时将值放置在正确的位置。 But it overwrites values that have yet to be moved. 但是它会覆盖尚未移动的值。 The only way to avoid this is to make a copy. 避免这种情况的唯一方法是进行复制。 So you could change the transpose method to work as follows: 因此,您可以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

But that's extremely convoluted and difficult to debug. 但是,这非常复杂且难以调试。 So the moral of the story is to just use self.state = list(zip(*self.state)) —it's fairly easy to read and hard to mess up—or what you already have in your transpose method. 因此,该故事的寓意是仅使用self.state = list(zip(*self.state)) -相当容易阅读且难以弄乱-或您的transpose方法中已经拥有的东西。


At a second glance, it appears that your transpose method does a double transposition. 乍一看,您的transpose方法似乎进行了两次转置。 And of course the transposition of a transposition of a matrix M is identical to M. You need to make the ceiling the inner loop smaller: 当然,矩阵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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM