12

多くの場合、負の数のラップアラウンド (つまりA[-2]、最後から 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__

私が提案しているのは、既存のカスタムクラスをサブクラス化することですが、listやのような組み込みではないことに注意してくださいdict。これは単に別のクラスの周りにユーティリティを作成するためのものであり、list型の通常の予想される操作を混乱させるためではありません。これは、操作が完了するまでの一定期間、特定のコンテキスト内で使用したいものです。コードのユーザーを混乱させるようなグローバルに異なる変更を行わないようにするのが最善です。

データ・モデル

物体。getitem(self、key)
self[key]の評価を実装するために呼び出されます。シーケンスタイプの場合、受け入れられるキーは整数とスライスオブジェクトである必要があります。負のインデックスの特別な解釈(クラスがシーケンスタイプをエミュレートする場合)は、 getitem()メソッド次第であることに注意してください。キーのタイプが不適切な場合、TypeErrorが発生する可能性があります。シーケンスのインデックスのセット外の値の場合(負の値の特別な解釈の後)、IndexErrorが発生する必要があります。マッピングタイプの場合、キーが欠落している(コンテナにない)場合、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
于 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 itemsしかしlst.__getslice__(-3,-1) == []

そして最後に:

lst[-2:1] == []、 しかしlst.__getslice__(-2,1) == lst[0:1]

驚くべき、興味深い、そしてまったく役に立たない。

于 2012-11-16T05:07:10.460 に答える