このようなことをしなければならないことはほとんどありませんが、少なくとも重複を取り除くことはできます。
[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
次に、サンプル コードでは 2 つのスライスの場合しか処理できませんが、実際のコードでは 2 つのスライス、またはスライスと int、または int とスライス、または 2 つの int、またはスライス、または int を処理する必要があります。スライス処理コードを除外できれば、何度も複製する必要はありません。
int-vs.-slice を処理するための 1 つの秘訣は[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)]
これはかなり単純に見えますが、ここでは 2 番目のインデックスを normal に転送することでごまかしてlist
いlist
ますlist
。しかし、延期する何らかの種類のインデックス可能な行オブジェクトがある場合 (そして、それらのオブジェクトを不必要に作成するために許容できない時間/スペースを無駄にしない場合)、同じチートを使用できます。
index パラメーターで型を切り替える必要があることに反対している場合は、はい、それは一般的に非 Pythonic のように見えますが、残念ながら、それは一般的にどのように機能するか__getitem__
です。通常の EAFTP ロジックを使用したい場合は使用できますが、複数の場所で 2 つの異なる API (たとえば、タプル用とスライス用)try
を試す必要がある場合は、読みやすくなるとは思いません。次のように、上部で「ダックタイプの切り替え」を行うことになります。[0]
.start
try:
idx[0]
except AttributeError:
idx = (idx, slice(None, None, None))
…など、これは通常の型切り替えの 2 倍のコードであり、通常のメリットはありません。