gpt4 book ai didi

python - 在 Python 中实现二维切片

转载 作者:太空宇宙 更新时间:2023-11-03 11:07:11 25 4
gpt4 key购买 nike

我正在用 Python 实现一个线性代数库(我知道可能存在一些东西,但我这样做是为了学习 Python 和考试所需的数学知识),我希望能够访问元素/像这样的矩阵的子集:

(我的矩阵类是元组的子类。)

  • M = Matrix([元素行列表])
  • M[1, 2] 获取 (1, 2) 处的元素
  • M[3] 获取第 3 行

这些很容易做到,但我也想实现切片,像这样:

  • M[:,:] 返回整个矩阵
  • M[1:6:2] 返回第 1、3 和 5 行
  • M[1:6:2, 0:2] 返回由与前两列相交的第 1、3 和 5 行组成的矩阵。

我已经这样做了,但我的回答似乎非常不符合 Pythonic:

def __getitem__ (self, idx):
if isinstance(idx, numbers.Integral):
# Code to return the row at idx
elif (isinstance(idx, tuple) and len(idx) == 2 and
all(isinstance(i, numbers.Integral) for i in idx)):
# Code to return element at idx
elif (isinstance(idx, tuple) and len(idx) == 2 and
all(isinstance(i, slice) for i in idx)):
# Code to parse slices

另一个问题是两个索引都必须是数字或切片,我不能混用。以这种方式执行此操作将需要另外两个 elif block ,这似乎是两个很多。代码已经很丑了。

我认为答案涉及鸭子类型(duck typing),但我不完全确定如何实现它。我一直在查看 try:except: block ,但我不确定如何链接这些 block ,而且我真的不想嵌套太多。

所以,谢谢你的阅读。实现此类功能的最佳方式是什么?

最佳答案

您几乎必须像这样做一些事情……但至少您可以删除一些重复项。

首先,将 [1,] 视为“第 1 行”的意思可能是合理的,就像 [1] 一样。 (numpy 就是这样做的。)这意味着您不需要 tuple-vs.-int 的东西;只需将 int 视为 1 元素元组即可。换句话说:

def __getitem__(self, idx):
if isinstance(idx, numbers.Integral):
idx = (idx, slice(None, None, None))
# now the rest of your code only needs to handle tuples

其次,虽然你的示例代码只处理两个切片的情况,但你的真实代码必须处理两个切片,或者一个切片和一个整数,或者一个整数和一个切片,或者两个整数,或者一个切片,或者一个诠释。如果您可以分解出切片处理代码,则无需一遍又一遍地复制它。

处理 int-vs.-slice 的一个技巧是将 [n] 视为包装器,本质上,[n:n+1][0],它可以让你进一步减少一切。 (这比这有点棘手,因为您必须对一般的负数或只是 -1 进行特殊处理,因为显然 n[-1] != n[-1 :0][0]。)对于一维数组,这可能不值得,但对于二维数组,它可能是值得的,因为这意味着当你处理列时,你总是有一个行列表,而不仅仅是一行。

另一方面,您可能希望在 __getitem____setitem__ 之间共享一些代码……这使得其中一些技巧要么不可能实现,要么变得更加困难。因此,需要权衡取舍。

无论如何,这是一个示例,它执行了我能想到的所有简化和预处理/后处理(可能比您想要的更多),因此最终您总是在查找一对切片:

class Matrix(object):
def __init__(self):
self.m = [[row + col/10. for col in range(4)] for row in range(4)]
def __getitem__(self, idx):
if isinstance(idx, (numbers.Integral, slice)):
idx = (idx, slice(None, None, None))
elif len(idx) == 1:
idx = (idx[0], slice(None, None, None))
rowidx, colidx = idx
rowslice, colslice = True, True
if isinstance(rowidx, numbers.Integral):
rowidx, rowslice = slice(rowidx, rowidx+1), False
if isinstance(colidx, numbers.Integral):
colidx, colslice = slice(colidx, colidx+1), False
ret = self.m[rowidx][colidx]
if not colslice:
ret = [row[0] for row in ret]
if not rowslice:
ret = ret[0]
return ret

或者如果您沿着另一个轴重构事物可能会更好:获取行,然后获取其中的列:

def _getrow(self, idx):
return self.m[idx]

def __getitem__(self, idx):
if isinstance(idx, (numbers.Integral, slice)):
return self._getrow(idx)
rowidx, colidx = idx
if isinstance(rowidx, numbers.Integral):
return self._getrow(rowidx)[colidx]
else:
return [row[colidx] for row in self._getrow(rowidx)]

这看起来简单多了,但我通过将第二个索引转发到普通的 list 来作弊,这只是因为我的底层存储是一个 list 列表的。但是,如果您有任何种可索引的行对象要遵循(并且它不会浪费 Not Acceptable 时间/空间来不必要地创建这些对象),您可以使用相同的欺骗。


如果您反对对 index 参数进行类型切换的需要,是的,这看起来通常不符合 Python 风格,但不幸的是,__getitem__ 通常是这样工作的。如果你想使用通常的 EAFTP try 逻辑,你可以,但我不认为当你必须尝试两个不同的 API 时它更具可读性(例如,[0] 用于元组,.start 用于切片)在多个地方。您最终会在顶部进行“鸭式切换”,如下所示:

try:
idx[0]
except AttributeError:
idx = (idx, slice(None, None, None))

...等等,这只是普通类型切换代码的​​两倍,没有任何通常的好处。

关于python - 在 Python 中实现二维切片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15668416/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com