12

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

下面是一个罐装的 2D 示例,但我对其他数据结构和其他数量的维度也有过几次同样的烦恼。

import numpy as np
A = np.random.randint(0, 2, (5, 10))

在此处输入图像描述

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()

在上面的切片中,我希望切片的任何负数都被视为相同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()

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

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

4

4 回答 4

6

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

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

数据模型

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

您甚至可以创建一个简单地将实例作为 arg 的包装器,并__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
于 2012-11-16T04:54:51.203 回答
4

虽然您可以list按照 jdi 的建议进行子类化,但 Python 的切片行为并不是任何人都希望您解决的问题。

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

参见:远距离行动

于 2012-11-16T05:14:36.537 回答
2

我认为这还不足以证明新类和包装东西的合理性。然后又是你的代码。

def foo(i, j, r=2):
  '''sum of neighbours within r steps of A[i,j]'''
  return A[i-r:abs(i+r+1), j-r:abs(j+r+1)].sum()   # ugly, but works?

(不赞成投票很有趣,所以我添加了更多选项)

我发现了一些非常出乎意料的事情(对我来说):__getslice__(i,j)不换行!相反,负索引只是被忽略,所以:

lst[1:3] == lst.__getslice__(1,3)

lst[-3:-1] == 2 next to last itemslst.__getslice__(-3,-1) == []

最后:

lst[-2:1] == [], 但lst.__getslice__(-2,1) == lst[0:1]

令人惊讶,有趣,完全没用。

于 2012-11-16T05:07:10.460 回答
1

如果这只需要应用在一些特定的操作中,一个简单而直截了当 if index>=0: do_something(array[i])的/if index<0: raise IndexError就可以了。

如果这需要更广泛地应用,它仍然是相同的逻辑,只是以这种或另一种方式包装。

于 2016-02-16T04:06:56.290 回答