2017-01-02 6 views
15

Мне показалось, что я понял основы обработки списка в python, но получал непредвиденную ошибку при использовании отрицательного шага на срезе, так как следующим образом:Ошибка в списке Python: [:: - 1] step on [: -1] slice

>>> a = list(range(10)) 
>>> a 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
>>> a[:-1] 
[0, 1, 2, 3, 4, 5, 6, 7, 8] 
>>> a[::-1] 
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 
>>> a[:-1:-1] 
[] 

(Обратите внимание, что это в настоящее время работает в Python 3.5)

Почему не [: - 1: -1] обратного шага через в [: - 1] срез в так же, как и через весь список с [:: - 1]?

Я понимаю, что вы также можете использовать list.reverse(), но лучше понять базовую функциональность среза python.

+1

Я думаю, что вы ищете '' а [-1 :: - 1] ''. Первый индекс дает начало, второй индекс дает конец, и вы хотите начать с индекса -1. – jakevdp

+0

@ekhumoro: Я сделал, и он работает. '[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]' – Josh

+1

@Josh. Как это отличается от 'a [:: - 1]'? – ekhumoro

ответ

19

Первый -1 в a[:-1:-1] не означает, что вы думаете.

Нарезка отрицательных начальных/конечных индексов не интерпретируется буквально. Вместо этого они используются для удобного обращения к концу списка (то есть они относятся к len(a)). Это происходит независимо от направления нарезки.

Это означает, что

a[:-1:-1] 

эквивалентно

a[:len(a)-1:-1] 

При опущено во время обратного квантования, индекс запуска по умолчанию len(a)-1, что делает выше эквивалент

a[len(a)-1:len(a)-1:-1] 

Это всегда дает пустой список, поскольку начальные и конечные индексы являются тот же и конечный индекс является исключительным.

Чтобы нарезать в обратном до, и в том числе, элемент Нулевое вы можете использовать любой из следующих обозначений:

>>> a[::-1] 
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 
>>> a[:None:-1] 
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 
>>> a[:-len(a)-1:-1] 
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 
+0

Как вы объясните разницу между результатами 'a [:: - 1]', 'a [: 0: -1]' и 'a [: - 1: -1]'? Или, говоря иначе: какое фактическое число представлено пустым конечным значением? – ekhumoro

+0

Я думаю, что '-len (a) -1' может использоваться для представления конца (' j', в 'a [i: j: k]'). Если 'a'' 'range (10)', то 'a [: - len (a) -1: -1]' производит '[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] '. Здесь: -len (a) -1' оценивается как -11; это отрицательно, поэтому вместо этого используется len (a) + -11' ([примечание 3] (https://docs.python.org/3/library/stdtypes.html#common-sequence-operations)), который оценивает -1. – Josh

0

ломтики языка Python, кажется довольно простым в первый, но их поведение на самом деле quite complex (примечания 3 и 5 - здесь). Если у вас есть кусочек a[i:j:k]:

  • Если i или j отрицательны, они относятся к индексу с конца a (так a[-1] относится к последнему элементу a)
  • Если i или j являются не указано, или None, они по умолчанию к концам a, но которые концов зависят от знака k:

    • если k является положительным, вы нарезка вперед, так i становится 0 и j становится len(a)
    • если k отрицательный, вы нарезка назад, так i становится len(a) и j становится элементом перед началом a.

      NB:jне может быть заменен на -1, так как делать это приведет к Python для лечения j как последнего элемента a, а не элемент (несуществующего) до a[0]. Чтобы получить желаемое поведение, вы должны использовать -len(a)-1 (или -(len(a)+1)) вместо j, что означает, что для перехода на a[j], срез начинается с последнего элемента a, идет влево для len(a) элементов, а затем оставлен еще один элемент, в результате чего до a начинается и, таким образом, включает a[0] в срез.

Поэтому a[:-1:-1] означает «идти от конца a, который является a[-1]i не определен и k отрицательный), чтобы последний элемент aj == -1), с размером шага от -1 ". i и j равны - вы начинаете и останавливаете нарезку в одном и том же месте - поэтому выражение оценивается как пустой список.

Для обратного a[:-1], вы можете использовать a[-2::-1]. Таким образом, срез начинается с предпоследнего элемента, a[-2] (начиная с a[:-1] не включает a[-1]) и идет назад до элемента «до» a[0], что означает, что a[0] включен в срез.

>>> a 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
>>> a[:-1] 
[0, 1, 2, 3, 4, 5, 6, 7, 8] 
>>> a[-2::-1] 
[8, 7, 6, 5, 4, 3, 2, 1, 0] 
+2

"' a [: - 1: -1] 'означает, что вы начинаете с начала (индекс 0) и идите назад, пока не нажмете последний элемент (индекс -1)." Не правда. См. Примечание № 5 в разделе [docs для операций последовательности] (https://docs.python.org/3/library/stdtypes.html#common-sequence-operations): во срезе 'a [i: j: k] '," Если i или j опущены или None, они становятся «конечными» значениями (конец которых зависит от знака k) ». Вот почему 'a [: 1: -1]' дает последние элементы len (a) - 2', а не первый элемент; это эквивалентно 'a [len (a) -1: 1: -1]'. – ThisSuitIsBlackNot

+1

@ThisSuitIsBlackNot Я думаю, что это ключ, который я не понимал: «какой конец зависит от знака k». Спасибо! –

+0

@ThisSuitIsBlackNot, Мэтт: ты совершенно прав, мне жаль, что у меня это так не так. Я удалю неверную информацию и переписал ее. – Josh

6

При вводе [1, 2, 3, ...][1:4:1] это то же самое, как [1, 2, 3, ...][slice(1, 4, 1)]. Таким образом, 1:4:1 является сокращением для slice объекта. slice Подпись - slice(stop) или slice(start, stop[, step]), и вы также можете использовать аргументы None.

:: -> slice(None, None, None) 
:4 -> slice(4) 
# and so on 

Предположим, у нас есть [a: b: c]. Правила для индексов будет выглядеть следующим образом:

  1. Первый c проверяется. По умолчанию +1, знак c указывает направление движения вперед или назад. Абсолютное значение c указывает размер шага.
  2. Запрашивается a. Когда c является положительным или None, по умолчанию для a является 0. Когда c отрицательный, значение по умолчанию для a: -1.
  3. Идентификационный номер b. Когда c является положительным или None, значение по умолчанию для b - len. Когда c является отрицательным значением по умолчанию для b, является -(len+1).

Примечание 1: выродился дольки в Python обрабатываются корректно:

  • индекс, который слишком велик или слишком мал заменяется len или 0.
  • верхняя граница, меньшая нижней границы, возвращает пустой список или строку или что-то еще (для положительного c).

Примечание 2: Грубо говоря, Python поднимает элементы в то время как это условие (a < b) if (c > 0) else (a > b) является True (обновление a += c на каждом шагу). Кроме того, все отрицательные индексы заменяются на len - index.

Если вы объедините эти правила и примечания, будет понятно, почему вы получили пустой список. В вашем случае:

In[1]: [1, 2, 3, 4, 5, 6][:-1:-1]  # `c` is negative so `a` is -1 and `b` is -1 
Out[1]: [] 

# it is the same as: 

In[2]: [1, 2, 3, 4, 5, 6][-1: -1: -1] # which will produce you an empty list 
Out[2]: [] 

Существует очень хорошая дискуссия о ломтике нотации: Explain Python's slice notation!

1

slice работает аналогично range в том, что когда вы делаете step аргумента отрицательное число, то start и stop аргументы работают в противоположном направлении.

>>> list(range(9, -1, -1)) == a[::-1] 
True 

Некоторые примеры, которые могут помочь сделать это более ясно:

>>> a[6:2:-2] 
[6, 4] 
>>> a[0:None:1] == a[::] 
True 
>>> a[-1:None:-1] == a[::-1] 
True 
>>> a[-2:None:-1] == a[:-1][::-1] 
True 
4

Я вообще считаю полезным нарезать range -объект (это возможно только в Python3 - в python2 range производит list и xrange не может быть нарезан), если мне нужно, чтобы увидеть, какие индексы используются для получения списка заданной длины:

>>> range(10)[::-1] 
range(9, -1, -1) 

>>> range(10)[:-1] 
range(0, 9) 

И в вашем последнем случае:

>>> range(10)[:-1:-1] 
range(9, 9, -1) 

Это также объясняет, что произошло. Первый индекс равен 9, но 9 не ниже индекса остановки 9 (обратите внимание, что в python индекс остановки равен исключен), поэтому он останавливается, не давая никакого элемента.

Обратите внимание, что индексация может также применяться последовательно:

>>> list(range(10))[::-1][:-1] # first reverse then exclude last item. 
[9, 8, 7, 6, 5, 4, 3, 2, 1] 
>>> list(range(10))[:-1][::-1] # other way around 
[8, 7, 6, 5, 4, 3, 2, 1, 0] 

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

  • Нет связанных вопросов^_^