2015-03-01 4 views
0

Я пытаюсь реализовать метод __getitem__ для класса в python. Я хочу попробовать что-то вроде ...Петля по индексам фрагмента

def __getitem__(self, key): 
    if isinstance(key, int): 
    return foo(key) 
    elif isinstance(key, slice): 
    return [foo(k) for k in key] 
    else: 
    raise TypeError("Invalid index.") 

... но я получаю ошибку 'slice' object is not iterable всякий раз, когда я на самом деле пытаюсь использовать его с кусочком. Каков правильный способ сделать это в python?

+0

Я не уверен, что какой-либо другой код будет полезен. В основном, я хочу, чтобы взять срез от n до m и перебрать значения от n до m, так же, как я могу перебрать диапазон (n, m). –

ответ

5

Используйте метод ломтика indices() и передайте результаты на range() или xrange().

>>> slice(2, 5).indices(7) 
(2, 5, 1) 
>>> range(*slice(2, 5).indices(7)) 
[2, 3, 4] 
>>> range(*slice(2, 10).indices(7)) 
[2, 3, 4, 5, 6] 
+0

Причина, по которой это лучший подход, заключается в том, что 'indices' будет обрабатывать отрицательные значения по умолчанию (' None') и индексы среза за пределы. В некоторых случаях очень легко ошибиться, если вы попытаетесь сделать это вручную. – Blckknght

1

Индексы могут работать, но вы также можете просто использовать старт, стоп и шаг среза, хотя они могут быть пустыми, так что вам нужно сделать что-то вроде этого ...

def __getitem__(self, key): 
    if isinstance(key, int): 
    return foo(key) 
    elif isinstance(key, slice): 
    start = key.start if key.start is not None else 0 
    stop = key.stop 
    step = key.step if key.step is not None else 1 
    return [foo(k) for k in range(start, stop, step)] 
    else: 
    raise TypeError("Invalid index.") 
1

проблема, с которой вы столкнулись, заключается в том, что, как указано в исключении, объект slice не является итерируемым. Я считаю, что лучший способ подумать о том, что это фрагмент, заключается в том, что это инструкция, которая присваивается итерации, чтобы определить, что она возвращает. Так что, если вы пытаетесь получить что-то вроде этого:

>>> L = [1, 2, 3, 4] 
>>> L[1:3] 
[2, 3] 

(очевидно, это упрощенный пример использования списков вместо пользовательского класса)

Кусочек, который передан __getitem__ будет иметь несколько атрибутов вы можете использовать:

>>> s = slice(1, 2, 3) 
>>> s.start, s.stop, s.step 

(1, 2, 3)

Если вы говорите s = slice(1, 2) то step атрибут Wi будет None. Вы можете использовать эту информацию, чтобы получить кусочек списка, кортеж или все, что вы хотите в __getitem__:

def __getitem__(self, i): 
    if isinstance(i, int): 
     return foo(i) 
    elif isinstance(i, slice): 
     return [foo(k) for k in self.bar[i]] # where bar is a list/tuple 

Причина это работает, потому что вызов self.bar[i] где i является срезом на самом деле не отличается от вызова self.bar[1:2], потому что 1:2 - это просто объект slice как есть i.

Надеюсь, это было полезно!

Смежные вопросы