Python列表/数组:禁用切片中的负索引环绕

wim*_*wim 11 python arrays numpy list slice

虽然我发现负数环绕(即A[-2]索引倒数第二个元素)在许多情况下非常有用,但当它发生在切片内时,它通常比一个有用的功能更令人烦恼,我经常希望有一种方法禁用该特定行为.

这是下面的一个罐装2D示例,但我与其他数据结构和其他维度的数量相同.

import numpy as np
A = np.random.randint(0, 2, (5, 10))
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

def foo(i, j, r=2):
  '''sum of neighbours within r steps of A[i,j]'''
  return A[i-r:i+r+1, j-r:j+r+1].sum()
Run Code Online (Sandbox Code Playgroud)

在上面的切片中,我宁愿切片的任何负数都将被视为相同None,而不是包装到数组的另一端.

由于包装,上面的其他好的实现在边界条件下给出了不正确的结果,并且需要某种补丁,例如:

def ugly_foo(i, j, r=2):
  def thing(n):
    return None if n < 0 else n
  return A[thing(i-r):i+r+1, thing(j-r):j+r+1].sum()
Run Code Online (Sandbox Code Playgroud)

我还尝试对数组或列表进行零填充,但它仍然不够优雅(需要相应地调整查找位置索引)并且效率低(需要复制数组).

我错过了一些像这样切片的标准技巧或优雅解决方案吗?我注意到python和numpy已经处理了一个很好地指定太大数字的情况 - 也就是说,如果索引大于数组的形状,它的行为就像它一样None.

jdi*_*jdi 6

我的猜测是您必须围绕所需的对象创建自己的子类包装器并重新实现__getitem__()以将负键转换为None,然后调用超类__getitem__

请注意,我的建议是对现有的自定义类进行子类化,而不是像listdict. 这只是为了围绕另一个类创建一个实用程序,而不是混淆一个list类型的正常预期操作。这将是您想要在特定上下文中使用一段时间直到您的操作完成的东西。最好避免进行全局不同的更改,这会使代码的用户感到困惑。

数据模型

目的。getitem (self, key)
调用以实现对 self[key] 的评估。对于序列类型,接受的键应该是整数和切片对象。请注意,负索引的特殊解释(如果类希望模拟序列类型)取决于 getitem () 方法。如果 key 的类型不合适,可能会引发 TypeError;如果序列的索引集之外的值(在对负值进行任何特殊解释之后),则应该引发 IndexError 。对于映射类型,如果缺少 key(不在容器中),则应引发 KeyError。

您甚至可以创建一个包装器,该包装器仅将实例作为参数,并将所有__getitem__()调用推迟到该私有成员,同时转换密钥,对于您不能或不想对类型进行子类化的情况,而只是想要任何序列对象的实用程序包装器。

后一个建议的快速示例:

class NoWrap(object):

    def __init__(self, obj, default=None):
        self._obj = obj 
        self._default = default

    def __getitem__(self, key):
        if isinstance(key, int):
            if key < 0:
                return self._default

        return self._obj.__getitem__(key)

In [12]: x = range(-10,10)
In [13]: x_wrapped = NoWrap(x)
In [14]: print x_wrapped[5]
-5
In [15]: print x_wrapped[-1]
None 
In [16]: x_wrapped = NoWrap(x, 'FOO')
In [17]: print x_wrapped[-1]
FOO
Run Code Online (Sandbox Code Playgroud)

  • 抱歉,但是标准 Python 语法的这种骇人听闻的做法比检查索引的边界更优雅,后者更清晰简洁? (2认同)

Zer*_*eus 4

虽然您可以按照 jdi 的建议进行子类化list,但任何人都不会希望您对 Python 的切片行为进行修改。

当代码的行为不符合预期时,更改它可能会导致使用您的代码的其他人感到非常头疼 - 并且可能需要一段时间才能查看您的子类的特殊方法以了解实际情况在。

参见:远距离行动