Мне нужно хорошее объяснение (ссылки - плюс) по нотации фрагментов Python.
На мой взгляд, это обозначение требует некоторого уточнения.
Он выглядит чрезвычайно мощным, но я не совсем понимаю.
Мне нужно хорошее объяснение (ссылки - плюс) по нотации фрагментов Python.
На мой взгляд, это обозначение требует некоторого уточнения.
Он выглядит чрезвычайно мощным, но я не совсем понимаю.
На самом деле это довольно просто:
a[start:stop] # items start through stop-1
a[start:] # items start through the rest of the array
a[:stop] # items from the beginning through stop-1
a[:] # a copy of the whole array
Также существует значение step
, которое можно использовать с любым из вышеперечисленных:
a[start:stop:step] # start through not past stop, by step
Ключевой момент, о котором следует помнить, - это то, что значение :stop
представляет первое значение, которого нет в выбранном фрагменте. Итак, разница между stop
и start
заключается в количестве выбранных элементов (если step
равно 1, по умолчанию).
Другая особенность состоит в том, что start
или stop
может быть отрицательным числом, что означает, что он отсчитывается от конца массива, а не от начала. Так:
a[-1] # last item in the array
a[-2:] # last two items in the array
a[:-2] # everything except the last two items
Точно так же step
может быть отрицательным числом:
a[::-1] # all items in the array, reversed
a[1::-1] # the first two items, reversed
a[:-3:-1] # the last two items, reversed
a[-3::-1] # everything except the last two items, reversed
Python добр к программистам, если их меньше, чем вы просите. Например, если вы запросите a[:-2]
, а a
содержит только один элемент, вы получите пустой список вместо ошибки. Иногда вы бы предпочли ошибку, поэтому вы должны знать, что это может произойти.
slice()
Оператор нарезки []
фактически используется в приведенном выше коде с объектом slice()
с использованием нотации :
(которая действительна только в пределах []
), то есть:
a[start:stop:step]
эквивалентно:
a[slice(start, stop, step)]
Объекты Slice также ведут себя немного по-разному в зависимости от количества аргументов, аналогично range()
, т.е. поддерживаются как slice(stop)
, так и slice(start, stop[, step])
. Чтобы пропустить указание данного аргумента, можно использовать None
, например, a[start:]
эквивалентно a[slice(start, None)]
или a[::-1]
эквивалентно a[slice(None, None, -1)]
.
В то время как нотация на основе :
очень полезна для простой нарезки, явное использование slice()
объектов упрощает программную генерацию нарезки.
None
на любое пустое пространство. Например, [None:None]
делает копию целиком. Это полезно, когда вам нужно указать конец диапазона с помощью переменной и включить последний элемент.
- person Mark Ransom; 16.01.2019
a[1::-1] # the first two items, reversed a[:-3:-1] # the last two items, reversed a[-3::-1] # everything except the last two items, reversed
Я проверил на консоли iPython, что ваш ответ правильный, но, похоже, он не соответствует шагу start: stop:. Я бы подумал, что перевернуть первые два элемента было бы a[:2:-1]
- person mLstudent33; 31.05.2021
Об этом говорится в руководстве по Python (прокрутите немного вниз, пока не дойдете до нужной части. о нарезке).
Арт-диаграмма ASCII также полезна для запоминания того, как работают срезы:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
Один из способов запомнить, как работают срезы, - это представить индексы как указывающие между символами, при этом левый край первого символа пронумерован 0. Затем правый край последнего символа строки n символов имеет индекс n.
a[-4,-6,-1]
будет yP
, но это ty
. Что всегда работает, так это думать символами или слотами и использовать индексирование как полуоткрытый интервал - открывать вправо при положительном шаге, открывать влево при отрицательном шаге.
- person aguadopd; 27.05.2019
x[:0]
, начиная с начала), поэтому вам нужно использовать небольшие массивы для особых случаев. : /
- person endolith; 06.07.2019
Перечисление возможностей, допускаемых грамматикой:
>>> seq[:] # [seq[0], seq[1], ..., seq[-1] ]
>>> seq[low:] # [seq[low], seq[low+1], ..., seq[-1] ]
>>> seq[:high] # [seq[0], seq[1], ..., seq[high-1]]
>>> seq[low:high] # [seq[low], seq[low+1], ..., seq[high-1]]
>>> seq[::stride] # [seq[0], seq[stride], ..., seq[-1] ]
>>> seq[low::stride] # [seq[low], seq[low+stride], ..., seq[-1] ]
>>> seq[:high:stride] # [seq[0], seq[stride], ..., seq[high-1]]
>>> seq[low:high:stride] # [seq[low], seq[low+stride], ..., seq[high-1]]
Конечно, если (high-low)%stride != 0
, то конечная точка будет немного ниже high-1
.
Если stride
отрицательно, порядок немного изменится, так как мы ведем обратный отсчет:
>>> seq[::-stride] # [seq[-1], seq[-1-stride], ..., seq[0] ]
>>> seq[high::-stride] # [seq[high], seq[high-stride], ..., seq[0] ]
>>> seq[:low:-stride] # [seq[-1], seq[-1-stride], ..., seq[low+1]]
>>> seq[high:low:-stride] # [seq[high], seq[high-stride], ..., seq[low+1]]
Расширенная нарезка (с запятыми и многоточиями) в основном используется только специальными структурами данных (например, NumPy); базовые последовательности их не поддерживают.
>>> class slicee:
... def __getitem__(self, item):
... return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
repr
- person wjandrea; 27.01.2019
__getitem__
: ваш пример эквивалентен apple[slice(4, -4, -1)]
.
- person chepner; 10.09.2019
Объясните нотацию фрагментов Python
Короче говоря, двоеточия (:
) в нотации нижнего индекса (subscriptable[subscriptarg]
) образуют нотацию среза, которая имеет необязательные аргументы start
, stop
, step
:
sliceable[start:stop:step]
Нарезка Python - это быстрый в вычислительном отношении способ методичного доступа к частям ваших данных. На мой взгляд, чтобы быть даже программистом на Python среднего уровня, это один из аспектов языка, с которым необходимо знать.
Для начала определимся с несколькими терминами:
start: начальный индекс среза, он будет включать элемент по этому индексу, если он не совпадает с stop, по умолчанию 0, то есть первый индекс. Если он отрицательный, значит начать
n
элементов с конца.stop: конечный индекс среза, он не включает элемент по этому индексу, по умолчанию используется длина разрезаемой последовательности, то есть до конец.
step: величина, на которую увеличивается индекс, по умолчанию 1. Если она отрицательна, вы выполняете нарезку итерации в обратном порядке.
Вы можете составить любое из этих положительных или отрицательных чисел. Смысл положительных чисел очевиден, но для отрицательных чисел, как и для индексов в Python, вы отсчитываете в обратном порядке от конца для start и stop, а для < em> step, вы просто уменьшаете индекс. Этот пример взят из руководства по документации, но я немного изменил его, чтобы указать на какой элемент в последовательности ссылается каждый индекс:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
Чтобы использовать нотацию срезов с последовательностью, которая ее поддерживает, вы должны включить хотя бы одно двоеточие в квадратные скобки, следующие за последовательностью (что на самом деле реализовать __getitem__
метод последовательности в соответствии с моделью данных Python.)
Обозначение среза работает следующим образом:
sequence[start:stop:step]
И помните, что есть значения по умолчанию для start, stop и step, поэтому для доступа к значениям по умолчанию просто опустите аргумент.
Обозначение среза для получения последних девяти элементов из списка (или любой другой последовательности, которая его поддерживает, например строки) будет выглядеть следующим образом:
my_list[-9:]
Когда я это вижу, я читаю часть в скобках как 9-ю от конца до конца. (На самом деле, я мысленно сокращаю его до -9, on)
Полное обозначение
my_list[-9:None:None]
и заменить значения по умолчанию (на самом деле, когда step
отрицательно, stop
по умолчанию -len(my_list) - 1
, поэтому None
для остановки на самом деле просто означает, что он переходит к любому конечному этапу, к которому это приведет):
my_list[-9:len(my_list):1]
Двоеточие, :
- это то, что сообщает Python, что вы даете ему фрагмент, а не обычный индекс. Вот почему идиоматический способ создания неглубокой копии списков в Python 2 таков:
list_copy = sequence[:]
И очистить их можно с помощью:
del my_list[:]
(Python 3 получает методы list.copy
и list.clear
.)
step
отрицательно, значения по умолчанию для start
и stop
меняются.По умолчанию, когда аргумент step
пуст (или None
), ему присваивается значение +1
.
Но вы можете передать отрицательное целое число, и список (или большинство других стандартных срезов) будет разрезан от конца до начала.
Таким образом, отрицательный срез изменит значения по умолчанию для start
и stop
!
Мне нравится поощрять пользователей читать исходный код, а также документацию. исходный код для объектов среза и эту логику можно найти здесь. Сначала мы определяем, является ли step
отрицательным:
step_is_negative = step_sign < 0;
Если это так, нижняя граница равна -1
, что означает, что мы разрезаем полностью до начала включительно, а верхняя граница равна длине минус 1, что означает, что мы начинаем с конца. (Обратите внимание, что семантика этого -1
отличается от -1
тем, что пользователи могут передавать в Python индексы, указывающие последний элемент.)
if (step_is_negative) { lower = PyLong_FromLong(-1L); if (lower == NULL) goto error; upper = PyNumber_Add(length, lower); if (upper == NULL) goto error; }
В противном случае step
положительно, и нижняя граница будет равна нулю, а верхняя граница (до которой мы поднимаемся, но не включая) - длину нарезанного списка.
else { lower = _PyLong_Zero; Py_INCREF(lower); upper = length; Py_INCREF(upper); }
Затем нам может потребоваться применить значения по умолчанию для start
и stop
- тогда значение по умолчанию для start
рассчитывается как верхняя граница, когда step
отрицательно:
if (self->start == Py_None) { start = step_is_negative ? upper : lower; Py_INCREF(start); }
и stop
, нижняя граница:
if (self->stop == Py_None) { stop = step_is_negative ? lower : upper; Py_INCREF(stop); }
Возможно, вам будет полезно отделить формирование среза от передачи его методу list.__getitem__
(это то, что делают квадратные скобки). Даже если вы не новичок в этом, он делает ваш код более читабельным, чтобы другие, которым, возможно, придется читать ваш код, могли легче понять, что вы делаете.
Однако вы не можете просто присвоить переменной некоторые целые числа, разделенные двоеточиями. Вам нужно использовать объект среза:
last_nine_slice = slice(-9, None)
Второй аргумент, None
, является обязательным, поэтому первый аргумент интерпретируется как аргумент start
иначе это был бы stop
аргумент.
Затем вы можете передать объект среза в свою последовательность:
>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]
Интересно, что диапазоны тоже берут срезы:
>>> range(100)[last_nine_slice]
range(91, 100)
Поскольку фрагменты списков Python создают новые объекты в памяти, еще одна важная функция, о которой следует помнить, - это itertools.islice
. Обычно вы хотите перебирать срез, а не просто статически создавать его в памяти. islice
идеально подходит для этого. Предостережение: он не поддерживает отрицательные аргументы для start
, stop
или step
, поэтому, если это проблема, вам может потребоваться заранее вычислить индексы или отменить итерацию.
length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)
и сейчас:
>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]
Тот факт, что срезы списка делают копию, является особенностью самих списков. Если вы нарезаете расширенные объекты, такие как Pandas DataFrame, он может возвращать представление оригинала, а не копию.
(start:stop)
нотация вводит в заблуждение, и (start_at:stop_before)
нотация, возможно, помешала мне найти эти вопросы и ответы.
- person WinEunuuchs2Unix; 30.09.2020
И пара вещей, которые не были сразу очевидны для меня, когда я впервые увидел синтаксис нарезки:
>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]
Простой способ перевернуть последовательность!
И если вам по какой-то причине нужен каждый второй элемент в обратной последовательности:
>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]
В Python 2.7
Нарезка в Python
[a:b:c]
len = length of string, tuple or list
c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.
a -- When c is positive or blank, default is 0. When c is negative, default is -1.
b -- When c is positive or blank, default is len. When c is negative, default is -(len+1).
Понимание присвоения индекса очень важно.
In forward direction, starts at 0 and ends at len-1
In backward direction, starts at -1 and ends at -len
Когда вы говорите [a: b: c], вы говорите, в зависимости от знака c (вперед или назад), начинать с a и заканчивать на b (исключая элемент с индексом b). Используйте указанное выше правило индексации и помните, что вы найдете только элементы в этом диапазоне:
-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1
Но этот диапазон продолжается в обоих направлениях бесконечно:
...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....
Например:
0 1 2 3 4 5 6 7 8 9 10 11
a s t r i n g
-9 -8 -7 -6 -5 -4 -3 -2 -1
Если ваш выбор a, b и c допускает перекрытие с диапазоном выше, когда вы проходите с использованием правил для a, b, c выше, вы либо получите список с элементами (затронутыми во время обхода), либо вы получите пустой список.
И последнее: если a и b равны, то вы также получите пустой список:
>>> l1
[2, 3, 4]
>>> l1[:]
[2, 3, 4]
>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]
>>> l1[:-4:-1] # a default is -1
[4, 3, 2]
>>> l1[:-3:-1] # a default is -1
[4, 3]
>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]
>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]
>>> l1[-100:-200:-1] # Interesting
[]
>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]
>>> l1[-1:-1:1]
[]
>>> l1[-1:5:1] # Interesting
[4]
>>> l1[1:-7:1]
[]
>>> l1[1:-7:-1] # Interesting
[3, 2]
>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]
a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2]
что приводит к [9]
- person Deviacium; 10.07.2017
Нашел эту замечательную таблицу на http://wiki.python.org/moin/MovingToPythonFromOtherLanguages
Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.
Index from rear: -6 -5 -4 -3 -2 -1 a=[0,1,2,3,4,5] a[1:]==[1,2,3,4,5]
Index from front: 0 1 2 3 4 5 len(a)==6 a[:5]==[0,1,2,3,4]
+---+---+---+---+---+---+ a[0]==0 a[:-2]==[0,1,2,3]
| a | b | c | d | e | f | a[5]==5 a[1:2]==[1]
+---+---+---+---+---+---+ a[-1]==5 a[1:-1]==[1,2,3,4]
Slice from front: : 1 2 3 4 5 : a[-2]==4
Slice from rear: : -5 -4 -3 -2 -1 :
b=a[:]
b==[0,1,2,3,4,5] (shallow copy of a)
Немного поработав с ним, я понимаю, что самое простое описание - это то же самое, что и аргументы в цикле for
...
(from:to:step)
Любые из них необязательны:
(:to:step)
(from::step)
(from:to)
Тогда для отрицательной индексации вам просто нужно добавить длину строки к отрицательным индексам, чтобы понять это.
В любом случае это работает для меня ...
Мне легче запомнить, как это работает, и тогда я смогу вычислить любую конкретную комбинацию старт / стоп / шаг.
Поучительно сначала понять range()
:
def range(start=0, stop, step=1): # Illegal syntax, but that's the effect
i = start
while (i < stop if step > 0 else i > stop):
yield i
i += step
Начинайте с start
, увеличивайте на step
, не доходите до stop
. Очень простой.
Об отрицательном шаге следует помнить, что stop
всегда является исключаемым концом, независимо от того, выше он или ниже. Если вам нужен один и тот же фрагмент в противоположном порядке, гораздо проще выполнить разворот отдельно: например, 'abcde'[1:-2][::-1]
отрезает один символ слева, два справа, затем меняет направление. (См. Также reversed()
.)
Нарезка последовательности такая же, за исключением того, что она сначала нормализует отрицательные индексы и никогда не может выходить за пределы последовательности:
ЗАДАЧА. В приведенном ниже коде была ошибка "никогда не выходить за пределы последовательности", когда abs (шаг)> 1; Я думаю, что исправил его, чтобы он был правильным, но это трудно понять.
def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
if start is None:
start = (0 if step > 0 else len(seq)-1)
elif start < 0:
start += len(seq)
if not 0 <= start < len(seq): # clip if still outside bounds
start = (0 if step > 0 else len(seq)-1)
if stop is None:
stop = (len(seq) if step > 0 else -1) # really -1, not last element
elif stop < 0:
stop += len(seq)
for i in range(start, stop, step):
if 0 <= i < len(seq):
yield seq[i]
Не беспокойтесь о is None
деталях - просто помните, что пропуск start
и / или stop
всегда дает вам полную последовательность.
Нормализация отрицательных индексов сначала позволяет отсчитывать начало и / или остановку с конца независимо: 'abcde'[1:-2] == 'abcde'[1:3] == 'bc'
несмотря на range(1,-2) == []
. Иногда нормализацию называют «по модулю длины», но обратите внимание, что она добавляет длину только один раз: например, 'abcde'[-53:42]
- это всего лишь вся строка.
this_is_how_slicing_works
- это не то же самое, что фрагмент Python. НАПРИМЕР. [0, 1, 2][-5:3:3]
получит [0] в python, но list(this_is_how_slicing_works([0, 1, 2], -5, 3, 3))
получит [1].
- person Eastsun; 29.10.2016
range(4)[-200:200:3] == [0, 3]
, но list(this_is_how_slicing_works([0, 1, 2, 3], -200, 200, 3)) == [2]
. Моя if 0 <= i < len(seq):
была попыткой реализовать просто никогда не выходить за рамки последовательности, но не подходит для шага ›1. Перепишу сегодня позже (с тестами).
- person Beni Cherniavsky-Paskin; 30.10.2016
Я сам использую метод "индексные точки между элементами", но один из способов описания этого, который иногда помогает другим понять, таков:
mylist[X:Y]
X - это индекс первого элемента, который вам нужен.
Y - это индекс первого элемента, который вам не нужен.
Index:
------------>
0 1 2 3 4
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
0 -4 -3 -2 -1
<------------
Slice:
<---------------|
|--------------->
: 1 2 3 4 :
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
: -4 -3 -2 -1 :
|--------------->
<---------------|
Надеюсь, это поможет вам смоделировать список на Python.
Ссылка: http://wiki.python.org/moin/MovingToPythonFromOtherLanguages
Обозначение нарезки Python:
a[start:end:step]
start
и end
отрицательные значения интерпретируются как относящиеся к концу последовательности.end
указывают позицию после последнего элемента, который должен быть включен.[+0:-0:1]
.start
и end
Обозначение распространяется на (numpy) матрицы и многомерные массивы. Например, чтобы нарезать целые столбцы, вы можете использовать:
m[::,0:2:] ## slice the first two columns
Срезы содержат ссылки, а не копии элементов массива. Если вы хотите сделать отдельную копию массива, вы можете использовать _ 9_.
Вот как я учу кусочков новичков:
Понимание разницы между индексированием и срезкой:
В Wiki Python есть удивительная картина, которая четко различает индексацию и нарезку.
Это список из шести элементов. Чтобы лучше понять нарезку, рассмотрите этот список как набор из шести блоков, помещенных вместе. В каждой коробке есть алфавит.
Индексирование похоже на работу с содержимым поля. Вы можете проверить содержимое любого ящика. Но вы не можете проверить содержимое сразу нескольких ящиков. Вы даже можете заменить содержимое коробки. Но вы не можете поместить два шара в одну коробку или заменить два шара за раз.
In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']
In [124]: alpha[0]
Out[124]: 'a'
In [127]: alpha[0] = 'A'
In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']
In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]
TypeError: list indices must be integers, not tuple
Нарезка - это как иметь дело с самими коробками. Вы можете взять первую коробку и поставить ее на другой стол. Чтобы поднять коробку, все, что вам нужно знать, это положение начала и конца коробки.
Вы даже можете взять первые три коробки, или последние две коробки, или все коробки от 1 до 4. Таким образом, вы можете выбрать любой набор коробок, если знаете начало и конец. Эти позиции называются начальными и конечными положениями.
Интересно то, что вы можете заменить сразу несколько коробок. Также вы можете разместить несколько коробок где угодно.
In [130]: alpha[0:1]
Out[130]: ['A']
In [131]: alpha[0:1] = 'a'
In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']
In [133]: alpha[0:2] = ['A', 'B']
In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']
In [135]: alpha[2:2] = ['x', 'xx']
In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']
Нарезка с шагом:
До сих пор вы постоянно выбирали ящики. Но иногда нужно подбирать незаметно. Например, вы можете забрать каждую вторую коробку. Вы даже можете забрать каждую третью коробку с конца. Это значение называется размером шага. Это представляет собой разрыв между вашими последовательными звукоснимателями. Размер шага должен быть положительным, если вы собираете коробки от начала до конца и наоборот.
In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [142]: alpha[1:5:2]
Out[142]: ['b', 'd']
In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']
In [144]: alpha[1:5:-2]
Out[144]: []
In [145]: alpha[-1:-5:2]
Out[145]: []
Как Python определяет отсутствующие параметры:
Если при нарезке вы не укажете какой-либо параметр, Python попытается вычислить его автоматически.
Если вы проверите исходный код CPython, вы найдете функцию PySlice_GetIndicesEx (), которая показывает индексы в срез для любых заданных параметров. Вот логический эквивалентный код на Python.
Эта функция принимает объект Python и дополнительные параметры для нарезки и возвращает начало, остановку, шаг и длину отрезка для запрошенного фрагмента.
def py_slice_get_indices_ex(obj, start=None, stop=None, step=None):
length = len(obj)
if step is None:
step = 1
if step == 0:
raise Exception("Step cannot be zero.")
if start is None:
start = 0 if step > 0 else length - 1
else:
if start < 0:
start += length
if start < 0:
start = 0 if step > 0 else -1
if start >= length:
start = length if step > 0 else length - 1
if stop is None:
stop = length if step > 0 else -1
else:
if stop < 0:
stop += length
if stop < 0:
stop = 0 if step > 0 else -1
if stop >= length:
stop = length if step > 0 else length - 1
if (step < 0 and stop >= start) or (step > 0 and start >= stop):
slice_length = 0
elif step < 0:
slice_length = (stop - start + 1)/(step) + 1
else:
slice_length = (stop - start - 1)/(step) + 1
return (start, stop, step, slice_length)
Это интеллект, который присутствует за срезами. Поскольку в Python есть встроенная функция slice, вы можете передать некоторые параметры и проверить, насколько умно она вычисляет недостающие параметры.
In [21]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [22]: s = slice(None, None, None)
In [23]: s
Out[23]: slice(None, None, None)
In [24]: s.indices(len(alpha))
Out[24]: (0, 6, 1)
In [25]: range(*s.indices(len(alpha)))
Out[25]: [0, 1, 2, 3, 4, 5]
In [26]: s = slice(None, None, -1)
In [27]: range(*s.indices(len(alpha)))
Out[27]: [5, 4, 3, 2, 1, 0]
In [28]: s = slice(None, 3, -1)
In [29]: range(*s.indices(len(alpha)))
Out[29]: [5, 4]
Примечание. Этот пост изначально был написан в моем блоге Интеллект, лежащий в основе фрагментов Python.
Вы также можете использовать назначение среза, чтобы удалить один или несколько элементов из списка:
r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]
Это просто для дополнительной информации ... Обратите внимание на список ниже
>>> l=[12,23,345,456,67,7,945,467]
Еще несколько уловок, чтобы перевернуть список:
>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
Как правило, написание кода с большим количеством жестко запрограммированных значений индекса приводит к беспорядку для чтения и обслуживания. Например, если вы вернетесь к коду через год, вы посмотрите на него и задаетесь вопросом, о чем вы думали, когда писали его. Показанное решение - это просто способ более четко указать, что на самом деле делает ваш код. Как правило, встроенный slice () создает объект среза, который можно использовать везде, где разрешен срез. Например:
>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]
Если у вас есть экземпляр среза s, вы можете получить дополнительную информацию о нем, просмотрев его атрибуты s.start, s.stop и s.step соответственно. Например:
>>> a = slice(10, 50, 2) >>> a.start 10 >>> a.stop 50 >>> a.step 2 >>>
Чтобы упростить задачу, помните, что срез имеет только одну форму :
s[start:end:step]
и вот как это работает:
s
: объект, который можно разрезатьstart
: первый индекс для начала итерацииend
: последний индекс, ПРИМЕЧАНИЕ, end
индекс не будет включен в полученный фрагментstep
: выбирать элемент каждый step
индексЕще один важный момент: все _8 _, _ 9_, step
можно опустить! И если они не указаны, будет использовано их значение по умолчанию: _11 _, _ 12 _, _ 13_ соответственно.
Итак, возможные варианты:
# Mostly used variations
s[start:end]
s[start:]
s[:end]
# Step-related variations
s[:end:step]
s[start::step]
s[::step]
# Make a copy
s[:]
ПРИМЕЧАНИЕ. Если start >= end
(учитывая только step>0
), Python вернет пустой фрагмент []
.
Вышеупомянутая часть объясняет основные функции работы среза, и она будет работать в большинстве случаев. Однако могут быть подводные камни, которых следует остерегаться, и эта часть объясняет их.
Первое, что сбивает с толку изучающих Python, это то, что индекс может быть отрицательным! Не паникуйте: отрицательный индекс означает обратный отсчет.
Например:
s[-5:] # Start at the 5th index from the end of array,
# thus returning the last 5 elements.
s[:-5] # Start at index 0, and end until the 5th index from end of array,
# thus returning s[0:len(s)-5].
Еще больше запутывает то, что step
тоже может быть отрицательным!
Отрицательный шаг означает повторение массива в обратном порядке: от конца к началу, с включением конечного индекса и исключением начального индекса из результата.
ПРИМЕЧАНИЕ: когда шаг отрицательный, значение по умолчанию для start
- len(s)
(в то время как end
не равно 0
, потому что s[::-1]
содержит s[0]
). Например:
s[::-1] # Reversed slice
s[len(s)::-1] # The same as above, reversed slice
s[0:len(s):-1] # Empty list
Будьте удивлены: slice не вызывает IndexError, когда индекс выходит за пределы допустимого диапазона!
Если индекс выходит за пределы допустимого диапазона, Python изо всех сил пытается установить для индекса значение 0
или len(s)
в зависимости от ситуации. Например:
s[:len(s)+5] # The same as s[:len(s)]
s[-len(s)-5::] # The same as s[0:]
s[len(s)+5::-1] # The same as s[len(s)::-1], and the same as s[::-1]
Давайте закончим этот ответ примерами, объясняя все, что мы обсуждали:
# Create our array for demonstration
In [1]: s = [i for i in range(10)]
In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [3]: s[2:] # From index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]
In [4]: s[:8] # From index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]
In [5]: s[4:7] # From index 4 (included) up to index 7(excluded)
Out[5]: [4, 5, 6]
In [6]: s[:-2] # Up to second last index (negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]
In [7]: s[-2:] # From second last index (negative index)
Out[7]: [8, 9]
In [8]: s[::-1] # From last to first in reverse order (negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
In [9]: s[::-2] # All odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]
In [11]: s[-2::-2] # All even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]
In [12]: s[3:15] # End is out of range, and Python will set it to len(s).
Out[12]: [3, 4, 5, 6, 7, 8, 9]
In [14]: s[5:1] # Start > end; return empty list
Out[14]: []
In [15]: s[11] # Access index 11 (greater than len(s)) will raise an IndexError
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]
IndexError: list index out of range
В предыдущих ответах не обсуждается нарезка многомерного массива, которая возможна с использованием известного пакета NumPy:
Нарезку также можно применять к многомерным массивам.
# Here, a is a NumPy array
>>> a
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>> a[:2, 0:3:2]
array([[1, 3],
[5, 7]])
«:2
» перед запятой работает с первым измерением, а «0:3:2
» после запятой работает со вторым измерением.
list
, а только на array
в Numpy
- person Mars Lee; 27.07.2019
Я лично думаю об этом как о цикле for
:
a[start:end:step]
# for(i = start; i < end; i += step)
Также обратите внимание, что отрицательные значения для start
и end
относятся к концу списка и вычисляются в приведенном выше примере с помощью given_index + a.shape[0]
.
#!/usr/bin/env python
def slicegraphical(s, lista):
if len(s) > 9:
print """Enter a string of maximum 9 characters,
so the printig would looki nice"""
return 0;
# print " ",
print ' '+'+---' * len(s) +'+'
print ' ',
for letter in s:
print '| {}'.format(letter),
print '|'
print " ",; print '+---' * len(s) +'+'
print " ",
for letter in range(len(s) +1):
print '{} '.format(letter),
print ""
for letter in range(-1*(len(s)), 0):
print ' {}'.format(letter),
print ''
print ''
for triada in lista:
if len(triada) == 3:
if triada[0]==None and triada[1] == None and triada[2] == None:
# 000
print s+'[ : : ]' +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] == None and triada[1] == None and triada[2] != None:
# 001
print s+'[ : :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] == None and triada[1] != None and triada[2] == None:
# 010
print s+'[ :{0:2d} : ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] == None and triada[1] != None and triada[2] != None:
# 011
print s+'[ :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] == None and triada[2] == None:
# 100
print s+'[{0:2d} : : ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] == None and triada[2] != None:
# 101
print s+'[{0:2d} : :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] != None and triada[2] == None:
# 110
print s+'[{0:2d} :{1:2d} : ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] != None and triada[2] != None:
# 111
print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif len(triada) == 2:
if triada[0] == None and triada[1] == None:
# 00
print s+'[ : ] ' + ' = ', s[triada[0]:triada[1]]
elif triada[0] == None and triada[1] != None:
# 01
print s+'[ :{0:2d} ] '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
elif triada[0] != None and triada[1] == None:
# 10
print s+'[{0:2d} : ] '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
elif triada[0] != None and triada[1] != None:
# 11
print s+'[{0:2d} :{1:2d} ] '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]
elif len(triada) == 1:
print s+'[{0:2d} ] '.format(triada[0]) + ' = ', s[triada[0]]
if __name__ == '__main__':
# Change "s" to what ever string you like, make it 9 characters for
# better representation.
s = 'COMPUTERS'
# add to this list different lists to experement with indexes
# to represent ex. s[::], use s[None, None,None], otherwise you get an error
# for s[2:] use s[2:None]
lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]
slicegraphical(s, lista)
Вы можете запустить этот скрипт и поэкспериментировать с ним, ниже приведены некоторые примеры, которые я получил из скрипта.
+---+---+---+---+---+---+---+---+---+
| C | O | M | P | U | T | E | R | S |
+---+---+---+---+---+---+---+---+---+
0 1 2 3 4 5 6 7 8 9
-9 -8 -7 -6 -5 -4 -3 -2 -1
COMPUTERS[ 4 : 7 ] = UTE
COMPUTERS[ 2 : 5 : 2 ] = MU
COMPUTERS[-5 : 1 :-1 ] = UPM
COMPUTERS[ 4 ] = U
COMPUTERS[-4 :-6 :-1 ] = TU
COMPUTERS[ 2 :-3 : 1 ] = MPUT
COMPUTERS[ 2 :-3 :-1 ] =
COMPUTERS[ : :-1 ] = SRETUPMOC
COMPUTERS[-5 : ] = UTERS
COMPUTERS[-5 : 0 :-1 ] = UPMO
COMPUTERS[-5 : :-1 ] = UPMOC
COMPUTERS[-1 : 1 :-2 ] = SEUM
[Finished in 0.9s]
При использовании отрицательного шага обратите внимание, что ответ сдвинут вправо на 1.
На мой взгляд, вы лучше поймете и запомните нотацию нарезки строк Python, если посмотрите на нее следующим образом (читайте дальше).
Давайте работать со следующей строкой ...
azString = "abcdefghijklmnopqrstuvwxyz"
Для тех, кто не знает, вы можете создать любую подстроку из azString
, используя обозначение azString[x:y]
Если исходить из других языков программирования, то здесь ставится под угрозу здравый смысл. Что такое x и y?
Мне пришлось сесть и запустить несколько сценариев в поисках техники запоминания, которая поможет мне вспомнить, что такое x и y, и поможет мне правильно нарезать строки с первой попытки.
Я пришел к выводу, что x и y следует рассматривать как граничные индексы, окружающие строки, которые мы хотим добавить. Таким образом, мы должны видеть выражение как azString[index1, index2]
или даже более ясное как azString[index_of_first_character, index_after_the_last_character]
.
Вот пример визуализации этого ...
Letters a b c d e f g h i j ...
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
┊ ┊
Indexes 0 1 2 3 4 5 6 7 8 9 ...
┊ ┊
cdefgh index1 index2
Итак, все, что вам нужно сделать, это установить index1 и index2 на значения, которые будут окружать желаемую подстроку. Например, чтобы получить подстроку «cdefgh», вы можете использовать azString[2:8]
, потому что индекс слева от «c» равен 2, а размер справа от «h» равен 8.
Помните, что мы устанавливаем границы. И эти границы - это позиции, в которых вы могли бы поместить некоторые скобки, которые будут обернуты вокруг подстроки, как это ...
a b [ c d e f g h ] i j
Этот трюк работает постоянно, и его легко запомнить.
Мой мозг, кажется, счастлив принять, что lst[start:end]
содержит start
-й элемент. Я могу даже сказать, что это «естественное предположение».
Но иногда закрадывается сомнение, и мой мозг просит подтверждения, что оно не содержит end
-й элемент.
В такие моменты я полагаюсь на эту простую теорему:
for any n, lst = lst[:n] + lst[n:]
Это красивое свойство сообщает мне, что lst[start:end]
не содержит end
-й элемент, потому что он находится в lst[end:]
.
Обратите внимание, что эта теорема верна для любого n
вообще. Например, вы можете проверить, что
lst = range(10)
lst[:-42] + lst[-42:] == lst
возвращает True
.
В Python самая простая форма нарезки следующая:
l[start:end]
где l
- некоторая коллекция, start
- инклюзивный индекс, а end
- исключительный индекс.
In [1]: l = list(range(10))
In [2]: l[:5] # First five elements
Out[2]: [0, 1, 2, 3, 4]
In [3]: l[-5:] # Last five elements
Out[3]: [5, 6, 7, 8, 9]
При нарезке с начала вы можете опустить нулевой индекс, а при нарезке до конца вы можете опустить окончательный индекс, поскольку он избыточен, поэтому не давайте подробностей:
In [5]: l[:3] == l[0:3]
Out[5]: True
In [6]: l[7:] == l[7:len(l)]
Out[6]: True
Отрицательные целые числа полезны при выполнении смещений относительно конца коллекции:
In [7]: l[:-1] # Include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
In [8]: l[-3:] # Take the last three elements
Out[8]: [7, 8, 9]
При нарезке можно указать индексы, выходящие за границы, например:
In [9]: l[:20] # 20 is out of index bounds, and l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [11]: l[-20:] # -20 is out of index bounds, and l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Имейте в виду, что в результате нарезки коллекции получается совершенно новая коллекция. Кроме того, при использовании нотации срезов в назначениях длина назначений срезов не обязательно должна быть одинаковой. Значения до и после назначенного среза будут сохранены, а коллекция будет сжиматься или увеличиваться, чтобы содержать новые значения:
In [16]: l[2:6] = list('abc') # Assigning fewer elements than the ones contained in the sliced collection l[2:6]
In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]
In [18]: l[2:5] = list('hello') # Assigning more elements than the ones contained in the sliced collection l [2:5]
In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]
Если вы опустите начальный и конечный индекс, вы сделаете копию коллекции:
In [14]: l_copy = l[:]
In [15]: l == l_copy and l is not l_copy
Out[15]: True
Если начальный и конечный индексы опущены при выполнении операции присваивания, все содержимое коллекции будет заменено копией того, на что имеется ссылка:
In [20]: l[:] = list('hello...')
In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']
Помимо базовой нарезки, также можно использовать следующие обозначения:
l[start:end:step]
где l
- коллекция, start
- инклюзивный индекс, end
- исключительный индекс, а step
- шаг, который можно использовать для получения каждого n-го элемента в l
.
In [22]: l = list(range(10))
In [23]: l[::2] # Take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]
In [24]: l[1::2] # Take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]
Использование step
дает полезный трюк для отмены коллекции в Python:
In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Также можно использовать отрицательные целые числа для step
в качестве следующего примера:
In[28]: l[::-2]
Out[28]: [9, 7, 5, 3, 1]
Однако использование отрицательного значения для step
может сильно запутать. Более того, чтобы быть Pythonic, вам следует избегать использования start
, end
и step
одним кусочком. Если это необходимо, рассмотрите возможность выполнения двух заданий (одно - нарезать, другое - шагать).
In [29]: l = l[::2] # This step is for striding
In [30]: l
Out[30]: [0, 2, 4, 6, 8]
In [31]: l = l[1:-1] # This step is for slicing
In [32]: l
Out[32]: [2, 4, 6]
Большинство предыдущих ответов проясняют вопросы о нотации срезов.
Для нарезки используется расширенный синтаксис индексации aList[start:stop:step]
, а основные примеры:
Дополнительные примеры нарезки: 15 расширенных фрагментов
Я хочу добавить один пример Hello, World!, который объясняет основы срезов для самых новичков. Это мне очень помогло.
У нас есть список из шести значений ['P', 'Y', 'T', 'H', 'O', 'N']
:
+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
0 1 2 3 4 5
Теперь самые простые фрагменты этого списка - его подсписки. Обозначение - [<index>:<index>]
, и суть в том, чтобы читать его так:
[ start cutting before this index : end cutting before this index ]
Теперь, если вы сделаете разрез [2:5]
из приведенного выше списка, произойдет следующее:
| |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
0 1 | 2 3 4 | 5
Вы сделали разрез перед элементом с индексом 2
и еще один разрез перед элементом с индексом 5
. Таким образом, результатом будет отрезок между этими двумя отрезками, список ['T', 'H', 'O']
.
Ниже приведен пример индекса строки:
+---+---+---+---+---+
| H | e | l | p | A |
+---+---+---+---+---+
0 1 2 3 4 5
-5 -4 -3 -2 -1
str="Name string"
Пример нарезки: [начало: конец: шаг]
str[start:end] # Items start through end-1
str[start:] # Items start through the rest of the array
str[:end] # Items from the beginning through end-1
str[:] # A copy of the whole array
Ниже приведен пример использования:
print str[0] = N
print str[0:2] = Na
print str[0:7] = Name st
print str[0:7:2] = Nm t
print str[0:-1:2] = Nm ti
Если вы считаете, что отрицательные индексы при нарезке сбивают с толку, вот очень простой способ подумать об этом: просто замените отрицательный индекс на len - index
. Так, например, замените -3 на len(list) - 3
.
Лучший способ проиллюстрировать внутреннюю работу нарезки - просто показать это в коде, реализующем эту операцию:
def slice(list, start = None, end = None, step = 1):
# Take care of missing start/end parameters
start = 0 if start is None else start
end = len(list) if end is None else end
# Take care of negative start/end parameters
start = len(list) + start if start < 0 else start
end = len(list) + end if end < 0 else end
# Now just execute a for-loop with start, end and step
return [list[i] for i in range(start, end, step)]
Я не думаю, что диаграмма учебника по Python (цитируется в различных другие ответы) хорош, поскольку это предложение работает для положительного шага, но не для отрицательного шага.
Это диаграмма:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
Судя по диаграмме, я ожидаю, что a[-4,-6,-1]
будет yP
, но это ty
.
>>> a = "Python"
>>> a[2:4:1] # as expected
'th'
>>> a[-4:-6:-1] # off by 1
'ty'
Что всегда работает, так это думать символами или слотами и использовать индексирование как полуоткрытый интервал - открывать вправо при положительном шаге, открывать влево при отрицательном шаге.
Таким образом, я могу думать о a[-4:-6:-1]
как о a(-6,-4]
в терминологии интервалов.
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
+---+---+---+---+---+---+---+---+---+---+---+---+
| P | y | t | h | o | n | P | y | t | h | o | n |
+---+---+---+---+---+---+---+---+---+---+---+---+
-6 -5 -4 -3 -2 -1 0 1 2 3 4 5
Основная техника нарезки состоит в том, чтобы определить начальную точку, точку остановки и размер шага, также известного как шаг.
Сначала мы создадим список значений, которые будут использоваться в нашем разрезе.
Создайте два списка для разрезания. Первый - это числовой список от 1 до 9 (Список A). Второй - это тоже числовой список от 0 до 9 (Список B):
A = list(range(1, 10, 1)) # Start, stop, and step
B = list(range(9))
print("This is List A:", A)
print("This is List B:", B)
Проиндексируйте цифру 3 из A и цифру 6 из B.
print(A[2])
print(B[6])
Базовая нарезка
Для нарезки используется расширенный синтаксис индексации: aList [start: stop: step]. Оба аргумента start и step по умолчанию равны none - единственный обязательный аргумент - stop. Вы заметили, что это похоже на то, как диапазон использовался для определения списков A и B? Это связано с тем, что объект среза представляет набор индексов, заданных диапазоном (начало, остановка, шаг). Документация Python 3.4.
Как видите, определение только остановки возвращает один элемент. Поскольку значение start по умолчанию равно none, это означает получение только одного элемента.
Важно отметить, что первый элемент - это индекс 0, не индекс 1. Вот почему мы используем 2 списка для этого упражнения. Элементы списка A нумеруются в соответствии с порядковой позицией (первый элемент - 1, второй элемент - 2 и т. Д.), В то время как элементы списка B - это числа, которые будут использоваться для их индексации ([0] для первого элемента 0, так далее.).
С помощью расширенного синтаксиса индексации мы получаем диапазон значений. Например, все значения извлекаются с двоеточием.
A[:]
Чтобы получить подмножество элементов, необходимо определить начальную и конечную позиции.
Учитывая шаблон aList [start: stop], извлеките первые два элемента из списка A.
Я был немного разочарован тем, что не нашел онлайн-источник или документацию Python, в которой точно описывается, что делает нарезка.
Я принял предложение Аарона Холла, прочитал соответствующие части исходного кода CPython и написал код Python, который выполняет нарезку аналогично тому, как это делается в CPython. Я протестировал свой код на Python 3 на миллионах случайных тестов в целочисленных списках.
Вы можете найти ссылки в моем коде на соответствующие функции в CPython.
# Return the result of slicing list x
# See the part of list_subscript() in listobject.c that pertains
# to when the indexing item is a PySliceObject
def slicer(x, start=None, stop=None, step=None):
# Handle slicing index values of None, and a step value of 0.
# See PySlice_Unpack() in sliceobject.c, which
# extracts start, stop, step from a PySliceObject.
maxint = 10000000 # A hack to simulate PY_SSIZE_T_MAX
if step == None:
step = 1
elif step == 0:
raise ValueError('slice step cannot be zero')
if start == None:
start = maxint if step < 0 else 0
if stop == None:
stop = -maxint if step < 0 else maxint
# Handle negative slice indexes and bad slice indexes.
# Compute number of elements in the slice as slice_length.
# See PySlice_AdjustIndices() in sliceobject.c
length = len(x)
slice_length = 0
if start < 0:
start += length
if start < 0:
start = -1 if step < 0 else 0
elif start >= length:
start = length - 1 if step < 0 else length
if stop < 0:
stop += length
if stop < 0:
stop = -1 if step < 0 else 0
elif stop > length:
stop = length - 1 if step < 0 else length
if step < 0:
if stop < start:
slice_length = (start - stop - 1) // (-step) + 1
else:
if start < stop:
slice_length = (stop - start - 1) // step + 1
# Cases of step = 1 and step != 1 are treated separately
if slice_length <= 0:
return []
elif step == 1:
# See list_slice() in listobject.c
result = []
for i in range(stop - start):
result.append(x[i+start])
return result
else:
result = []
cur = start
for i in range(slice_length):
result.append(x[cur])
cur += step
return result
[lower bound : upper bound : step size]
I- Преобразуйте upper bound
и lower bound
в общие знаки.
II- Затем проверьте, является ли step size
положительным или отрицательным значением.
(i) Если step size
является положительным значением, upper bound
должно быть больше lower bound
, в противном случае печатается empty string
. Например:
s="Welcome"
s1=s[0:3:1]
print(s1)
Выход:
Wel
Однако, если мы запустим следующий код:
s="Welcome"
s1=s[3:0:1]
print(s1)
Он вернет пустую строку.
(ii) Если step size
, если отрицательное значение, upper bound
должно быть меньше lower bound
, иначе будет напечатано empty string
. Например:
s="Welcome"
s1=s[3:0:-1]
print(s1)
Выход:
cle
Но если мы запустим следующий код:
s="Welcome"
s1=s[0:5:-1]
print(s1)
Результатом будет пустая строка.
Таким образом в коде:
str = 'abcd'
l = len(str)
str2 = str[l-1:0:-1] #str[3:0:-1]
print(str2)
str2 = str[l-1:-1:-1] #str[3:-1:-1]
print(str2)
В первом str2=str[l-1:0:-1]
upper bound
меньше lower bound
, поэтому печатается dcb
.
Однако в str2=str[l-1:-1:-1]
upper bound
не меньше lower bound
(при преобразовании lower bound
в отрицательное значение, равное -1
: поскольку index
последнего элемента равно -1, а также 3) .
Легко понять, можно ли связать срез с range
, который дает индексы. Мы можем разделить нарезки на следующие две категории:
[i:j]
или [i:j:k]
(k> 0)Предположим, что последовательность равна s=[1,2,3,4,5]
.
0<i<len(s)
и 0<j<len(s)
, то [i:j:k] -> range(i,j,k)
Например, [0:3:2] -> range(0,3,2) -> 0, 2
i>len(s)
или j>len(s)
, то i=len(s)
или j=len(s)
Например, [0:100:2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4
i<0
или j<0
, то i=max(0,len(s)+i)
или j=max(0,len(s)+j)
Например, [0:-3:2] -> range(0,len(s)-3,2) -> range(0,2,2) -> 0
Другой пример: [0:-1:2] -> range(0,len(s)-1,2) -> range(0,4,2) -> 0, 2
i
не указан, то i=0
Например, [:4:2] -> range(0,4,2) -> range(0,4,2) -> 0, 2
j
не указан, то j=len(s)
Например, [0::2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4
[i:j:k]
(k‹ 0)Предположим, что последовательность равна s=[1,2,3,4,5]
.
0<i<len(s)
и 0<j<len(s)
, то [i:j:k] -> range(i,j,k)
Например, [5:0:-2] -> range(5,0,-2) -> 5, 3, 1
i>len(s)
или j>len(s)
, то i=len(s)-1
или j=len(s)-1
Например, [100:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2
i<0
или j<0
, то i=max(-1,len(s)+i)
или j=max(-1,len(s)+j)
Например, [-2:-10:-2] -> range(len(s)-2,-1,-2) -> range(3,-1,-2) -> 3, 1
i
не указан, то i=len(s)-1
Например, [:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2
j
не указан, то j=-1
Например, [2::-2] -> range(2,-1,-2) -> 2, 0
Другой пример: [::-1] -> range(len(s)-1,-1,-1) -> range(4,-1,-1) -> 4, 3, 2, 1, 0