Вы должны сделать что-то подобное… но, по крайней мере, вы можете удалить некоторое дублирование.
Во-первых, вероятно, разумно считать, что [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
из list
s. Но если у вас есть любой объект индексируемой строки для отсрочки (и это не тратит неприемлемое время/пространство на создание этих объектов без необходимости), вы можете использовать тот же чит.
Если вы возражаете против необходимости переключения типа в параметре index, да, в целом это кажется непитоновским, но, к сожалению, так обычно работает __getitem__
. Если вы хотите использовать обычную логику EAFTP try
, вы можете это сделать, но я не думаю, что это более читабельно, когда вам нужно попробовать два разных API (например, [0]
для кортежей и .start
для слайсов) в нескольких местах. В конечном итоге вы выполняете «переключение типа утки» вверху, например так:
try:
idx[0]
except AttributeError:
idx = (idx, slice(None, None, None))
… и так далее, и это вдвое больше кода, чем обычное переключение типов без каких-либо обычных преимуществ.
person
abarnert
schedule
27.03.2013
numpy
, самое заметное из того, что уже существует. Он делает все, что вы хотите, и даже больше, и, вероятно, сделает ваш код немного легче для чтения и намного быстрее. Я не хотел упоминать об этом сразу, потому что не хотел отговаривать вас от веселья, придумывая свои собственные проекты… но я также хочу убедиться, что вы не пропустите удовольствие от игры сnumpy
. - person abarnert   schedule 28.03.2013