Розуміння позначень фрагментів


3281

Мені потрібно гарне пояснення (посилання є плюсом) щодо позначення фрагмента Python.

Для мене це позначення потрібно трохи підібрати.

Це виглядає надзвичайно потужно, але я не зовсім обернув це.

Відповіді:


4498

Це дуже просто:

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)]

Об'єкти зрізів також поводяться дещо по-різному, залежно від кількості аргументів, аналогічно range(), тобто обидва slice(stop)і slice(start, stop[, step])підтримуються. Щоб пропустити вказівку заданого аргументу, можна скористатися Noneтак, що, наприклад a[start:], еквівалентно a[slice(start, None)]або a[::-1]еквівалентно a[slice(None, None, -1)].

Хоча :позначення на основі дуже корисні для простого нарізання, явне використання slice()об'єктів спрощує програмну генерацію нарізки.


122
Нарізання вбудованих типів повертає копію, але це не є універсальним. Зокрема, нарізка масивів NumPy повертає вигляд, який розділяє пам'ять з оригіналом.
Бені Чернявський-Паскін

43
Це гарна відповідь з голосами, щоб довести це, але пропускає одне: ви можете замінити Noneбудь-який з порожніх пробілів. Наприклад, [None:None]робить цілу копію. Це корисно, коли вам потрібно вказати кінець діапазону за допомогою змінної та потрібно включити останній елемент.
Марк Викуп

6
Що мене справді дратує, це те, що python каже, що коли ви не встановлюєте початок і кінець, вони за замовчуванням становлять 0 і тривалість послідовності. Так, теоретично, коли ви використовуєте "abcdef" [:: - 1], його слід перетворити на "abcdef" [0: 6: -1], але ці два вирази не отримують однакового результату. Я відчуваю, що чогось не вистачає в документації на python з моменту створення мови.
axell13

6
І я знаю, що "abcdef" [:: - 1] перетворюється на "abcdef" [6: -7: -1], тож найкращим способом пояснення було б: нехай len буде довжиною послідовності. Якщо крок позитивний , значення за замовчуванням для початку та кінця дорівнюють 0 та len . В іншому випадку, якщо крок негативний , типовими для початку та кінця є len та - len - 1.
axell13

4
Оскільки stackoverflow.com/questions/39241529/… позначено справжнім, це має сенс додати розділ із тим, що delозначає позначення wrt-фрагментів. Зокрема, del arr[:]це не відразу очевидно ("arr [:] робить копію, тому дель видаляє цю копію ???" тощо)
khazhyk

537

Підручник з Python розповідає про це (прокрутіть трохи вниз, поки ви не перейдете до частини про нарізання).

Художня діаграма ASCII також корисна для запам'ятовування роботи зрізів:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

Один із способів запам’ятати, як працюють фрагменти, - це думати про індекси як на вказівку між символами, лівий край першого символу пронумерований 0. Тоді правий край останнього символу рядка з n символів має індекс n .


10
Ця пропозиція працює у позитивному кроці, але не є негативною. З діаграми я очікую, що a[-4,-6,-1]це буде, yPале воно є ty. Що завжди працює - це мислити символами чи слотами та використовувати індексацію як напіввідкритий інтервал - справа-відкрито, якщо позитивний крок, ліво-відкрито, якщо негативний крок.
агуадопд

Але немає можливості згорнутись до порожнього набору, починаючи з кінця (як x[:0]це робиться, коли починається з початку), тому вам доведеться робити невеликі масиви спеціального випадку. : /
ендоліти

412

Перерахування можливостей, дозволених граматикою:

>>> 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)'

Насправді все ще залишається щось, наприклад, якщо я набираю "яблуко" [4: -4: -1] я отримую "elp", python може переводити -4 на 1, можливо?
liyuan

зауважте, що фони застаріли на користьrepr
wjandrea

@liyuan Реалізація типу __getitem__є; ваш приклад еквівалентний apple[slice(4, -4, -1)].
чепнер

320

Наведені вище відповіді не обговорюють призначення фрагментів. Щоб зрозуміти призначення фрагментів, корисно додати іншу концепцію до мистецтва ASCII:

                +---+---+---+---+---+---+
                | P | y | t | h | o | n |
                +---+---+---+---+---+---+
Slice position: 0   1   2   3   4   5   6
Index position:   0   1   2   3   4   5

>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
 'P'
>>> p[5]
 'n'

# Slicing gives lists
>>> p[0:1]
 ['P']
>>> p[0:2]
 ['P','y']

Одне евристичне, для відрізка від нуля до n подумайте: «нуль - це початок, почніть з початку і візьміть n елементів у списку».

>>> p[5] # the last of six items, indexed from zero
 'n'
>>> p[0:5] # does NOT include the last item!
 ['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
 ['P','y','t','h','o','n']

Інша евристика - "для будь-якого фрагмента замініть початок на нуль, застосуйте попередній евристичний, щоб отримати кінець списку, а потім підрахуйте перше число назад, щоб відрізати елементи з початку"

>>> p[0:4] # Start at the beginning and count out 4 items
 ['P','y','t','h']
>>> p[1:4] # Take one item off the front
 ['y','t','h']
>>> p[2:4] # Take two items off the front
 ['t','h']
# etc.

Перше правило присвоєння фрагмента полягає в тому, що оскільки фрагмент повертає список, для призначення фрагмента потрібен список (або інший ітерабельний):

>>> p[2:3]
 ['t']
>>> p[2:3] = ['T']
>>> p
 ['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

Друге правило присвоєння фрагментів, яке ви також можете бачити вище, полягає в тому, що будь-яка частина списку повертається шляхом індексації фрагментів, це та сама частина, яка змінюється присвоєнні фрагмента:

>>> p[2:4]
 ['T','h']
>>> p[2:4] = ['t','r']
>>> p
 ['P','y','t','r','o','n']

Третє правило присвоєння фрагментів - присвоєний список (ітерабельний) не повинен мати однакову довжину; індексований фрагмент просто вирізається і масово замінюється тим, що йому призначено:

>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
 ['P','y','s','p','a','m','o','n']

Найскладніша частина, до якої звикнути, - це присвоєння порожнім скибочкам. Використовуючи евристичні 1 і 2, можна просто обвести голову, вказуючи порожній фрагмент:

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []

А потім, як тільки ви це побачили, присвоєння фрагменту порожньому фрагменту теж має сенс:

>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
 ['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
 ['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
 ['P','y','t','h','x','y','o','n'] # The result is longer still

Зауважте, що, оскільки ми не змінюємо другий номер фрагмента (4), вставлені елементи завжди укладаються прямо проти 'o', навіть коли ми призначаємо порожній фрагмент. Отже, позиція для призначення порожнього фрагмента - це логічне розширення позицій для призначення не порожнього фрагмента.

Трохи підкріпившись, що станеться, коли ви продовжуєте процес нашого підрахунку часу?

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []

З нарізкою, як тільки ви закінчите, ви закінчите; вона не починає нарізати назад. У Python ви не отримуєте негативних кроків, якщо ви прямо не попросите їх за допомогою негативного числа.

>>> p[5:3:-1]
 ['n','o']

Існують деякі дивні наслідки правила "як тільки ти закінчиш, ти закінчиш":

>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []
>>> p[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

Насправді, порівняно з індексуванням, нарізка Python є химерним чином: помилка:

>>> p[100:200]
 []
>>> p[int(2e99):int(1e99)]
 []

Іноді це може стати в нагоді, але це також може призвести до дещо дивної поведінки:

>>> p
 ['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
 ['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']

Залежно від вашої заявки, це може бути ... чи не може бути ... тим, на що ви сподівалися там!


Нижче текст моєї оригінальної відповіді. Він був корисний багатьом людям, тому я не хотів його видаляти.

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

Це також може уточнити різницю між нарізкою та індексуванням.


246

Поясніть позначення фрагмента Python

Коротше кажучи, колони ( :) в індексному позначенні ( subscriptable[subscriptarg]) робить зріз позначення - у якого є необов'язкові аргументи, start, stop, step:

sliceable[start:stop:step]

Нарізка Python - це обчислювально швидкий спосіб методичного доступу до частин ваших даних. На мою думку, щоб бути навіть проміжним програмістом Python, це один із аспектів мови, з яким потрібно знати.

Важливі визначення

Для початку давайте визначимося з кількома термінами:

початок: початковий індекс фрагмента, він буде включати елемент цього індексу, якщо він не такий, як зупинка , за замовчуванням 0, тобто перший індекс. Якщо це негативно, це означає починати nелементи з кінця.

Стоп: індекс закінчення фрагмента, він не включає елемент цього індексу, за замовчуванням до довжини нарізаної послідовності, тобто до кінця, включаючи кінець.

крок: сума, на яку збільшується індекс, за замовчуванням до 1. Якщо вона від’ємна, ви перерізаєте ітерабельний зворотний бік.

Як працює індексація

Ви можете зробити будь-яке з цих позитивних чи від’ємних чисел. Значення додатних чисел прямо зрозуміло, але для від'ємних чисел, подібно до індексів у Python, ви рахуєте назад від кінця для початку та зупинки , а для кроку ви просто зменшуєте свій індекс. Цей приклад із підручника документації , але я його трохи змінив, щоб вказати на який елемент у послідовності посилається кожен індекс:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5 
  -6  -5  -4  -3  -2  -1

Як працює нарізка

Щоб використовувати позначення зрізів з послідовністю, яка його підтримує, ви повинні включити принаймні одну двокрапку в квадратні дужки, які слідують за послідовністю (що фактично реалізує __getitem__метод послідовності, відповідно до моделі даних Python .)

Позначення фрагментів працює так:

sequence[start:stop:step]

І пам’ятайте, що для запуску , зупинки та кроку є типові параметри , тому для доступу до значень за замовчуванням просто не залишайте аргумент.

Позначення зрізів для отримання останніх дев'яти елементів зі списку (або будь-якої іншої послідовності, яка його підтримує, як рядок) виглядатиме так:

my_list[-9:]

Коли я це бачу, читаю частину в дужках як "9-ту з кінця, до кінця". (Насправді я подумки скорочую це як «-9, далі»)

Пояснення:

Повне позначення є

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, він може повернути перегляд оригіналу, а не копію.


146

І ще кілька речей, які мені не були очевидними, коли я вперше побачив синтаксис нарізки:

>>> 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]

100

У 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 (виключаючи елемент у bth індексі). Скористайтеся правилом індексації вище та пам’ятайте, що ви знайдете лише елементи в цьому діапазоні:

-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]

2
ще один цікавий приклад: a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2]результат[9]
Девіацій

96

Знайшов цей чудовий стіл на 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)

65

Трохи використавши його, я зрозумів, що найпростіший опис полягає в тому, що він точно такий же, як аргументи в forциклі ...

(from:to:step)

Будь-який із них не є обов'язковим:

(:to:step)
(from::step)
(from:to)

Тоді негативне індексування просто потрібно, щоб ви додали довжину рядка до негативних індексів, щоб зрозуміти це.

Це все одно для мене працює ...


52

Мені легше запам'ятати, як це працює, і тоді я можу визначити будь-яку конкретну комбінацію старт / стоп / крок.

Потрібно зрозуміти 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().)

Нарізка послідовності однакова, за винятком того, що вона спочатку нормалізує негативні показники, і вона ніколи не може вийти за межі послідовності

TODO : У наведеному нижче коді виникла помилка з "ніколи не виходити за межі послідовності", коли 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]це лише весь рядок.


3
Це this_is_how_slicing_worksне те саме, що пітон шматочок. EG [0, 1, 2][-5:3:3]отримає [0] в python, але list(this_is_how_slicing_works([0, 1, 2], -5, 3, 3))отримає [1].
Eastsun

@Eastsun На жаль, ти маєш рацію! Чіткіший випадок: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. Я перепишу це пізніше сьогодні (з тестами).
Бені Чернявський-Паскін

40

Я сам використовую метод "індексних точок між елементами", щоб думати про це, але один із способів його опису, який іноді допомагає іншим зрозуміти це:

mylist[X:Y]

X - індекс першого елемента, який ви хочете.
Y - індекс першого елемента, який ви не хочете.


40
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


38

Позначення нарізки пітона:

a[start:end:step]
  • Для startіend , негативні значення інтерпретуються як відносні до кінця послідовності.
  • Позитивні показники для endпозначення позиції після включення останнього елемента.
  • Порожні значення по замовчуванням наступним чином : [+0:-0:1].
  • Використання негативного кроку повертає інтерпретацію startтаend

Позначення поширюється на (numpy) матриці та багатовимірні масиви. Наприклад, для розрізання цілих стовпців ви можете використовувати:

m[::,0:2:] ## slice the first two columns

У фрагментах містяться посилання, а не копії елементів масиву. Якщо ви хочете зробити окрему копію масиву, ви можете використовувати deepcopy().


34

Ви також можете використовувати призначення зрізів, щоб видалити один або кілька елементів зі списку:

r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]

33

Це лише для додаткової інформації ... Розгляньте список нижче

>>> 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]

33

Ось як я вчу скибочок для новачків:

Розуміння різниці між індексуванням та нарізкою:

У 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 є вбудована функція, яка називається фрагмент, ви можете передавати деякі параметри і перевірити, наскільки розумно він обчислює відсутні параметри.

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]

Примітка. Ця публікація спочатку була написана в моєму блозі "Розвідка за фрагментами пітона" .


29

Як правило, написання коду з великою кількістю твердо кодованих значень індексу призводить до зручності читання та обслуговування. Наприклад, якщо через рік ви повернетесь до коду, ви подивитесь на нього і здивуєтеся, що ви думали, коли його писали. Показане рішення - це просто спосіб більш чітко визначити, чим насправді займається ваш код. Взагалі, вбудований фрагмент () створює об'єкт фрагмента, який можна використовувати де завгодно. Наприклад:

>>> 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.start, s.stop та s.step відповідно. Наприклад:

>>> a = slice(10, 50, 2)
>>> a.start
10
>>> a.stop
50
>>> a.step
2
>>>

25

1. Позначення фрагмента

Щоб зробити це просто, пам’ятайте, що фрагмент має лише одну форму:

s[start:end:step]

і ось як це працює:

  • s: предмет, який можна нарізати
  • start: перший індекс для запуску ітерації
  • end: останній індекс, Зверніть увагу, що endіндекс не буде включений до результату
  • step: вибрати елемент кожного stepіндексу

Інший імпорт річ: все start, end, stepможе бути опущена! І якщо вони опущені, буде використано їх значення по замовчуванням: 0, len(s),1 відповідно.

Тому можливі варіанти:

# 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 поверне порожній фрагмент[] .

2. Підводні камені

Вищевказана частина пояснює основні особливості того, як працює фрагмент, і він буде працювати в більшості випадків. Однак можуть бути підводні камені, на які слід стежити, і ця частина пояснює їх.

Негативні показники

Найперше, що бентежить учнів 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

Помилка поза діапазоном?

Будьте здивовані: фрагмент не піднімає 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]

3. Приклади

Давайте закінчимо цю відповідь на прикладах, пояснивши все, що ми обговорили:

# 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

24

Попередні відповіді не обговорюють розмірність багатовимірного масиву, який можливий за допомогою відомого пакету 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" після коми працює над другим виміром.


4
Просто дружнє нагадування про те, що ви не можете цього робити на Python, listа лише arrayв Numpy
Mars Lee

15
#!/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.


14

Мій мозок здається щасливим прийняти, що 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.


12

На мою думку, ви краще зрозумієте і запам’ятаєте нотацію нарізки рядка Python, якщо подивитесь на неї наступним чином (читайте далі).

Давайте працювати з наступним рядком ...

azString = "abcdefghijklmnopqrstuvwxyz"

Для тих, хто не знає, ви можете створити будь-яку підрядку, azStringвикористовуючи позначенняazString[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.

Пам'ятайте, що ми встановлюємо межі. І ці межі - це позиції, де ви можете розмістити кілька дужок, які будуть обернуті навколо підрядок таким чином ...

ab [ cdefgh ] ij

Цей трюк працює весь час і його легко запам’ятати.


11

Більшість попередніх відповідей очищає питання щодо позначення фрагментів.

Розширений синтаксис індексації, який використовується для нарізки, є aList[start:stop:step]основними прикладами:

Введіть тут опис зображення:

Більше прикладів нарізки: 15 розширених фрагментів


10

У 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може стати дуже заплутаним. Крім того, для того , щоб бути віщим , слід уникати використання 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]

10

Я хочу додати один 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'].


10

Я особисто думаю про це як forцикл:

a[start:end:step]
# for(i = start; i < end; i += step)

Також зауважте, що від'ємні значення для startта endвідносно кінця списку та обчислені у наведеному вище прикладі given_index + a.shape[0].


8

Нижче наведено приклад індексу рядка:

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

str="Name string"

Приклад нарізки: [start: end: step]

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

5

Якщо ви вважаєте, що негативні показники нарізки є заплутаними, ось дуже простий спосіб подумати над цим: просто замініть негативний індекс на 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)]

4

Основна техніка нарізки - визначити початкову точку, точку зупинки та розмір кроку - також відомий як крок.

По-перше, ми створимо список значень, які слід використовувати в нашій нарізці.

Створіть два списки для скибочки. Перший - це числовий список від 1 до 9 (Список А). Другий - це також числовий список від 0 до 9 (Список В):

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]. Аргумент старту і крок аргумент за замовчуванням - немає, єдиний необхідний аргумент - стоп. Ви помітили, що це схоже на те, як діапазон використовувався для визначення списків А і В? Це відбувається тому, що об'єкт зрізу представляє набір індексів, визначених діапазоном (початок, зупинка, крок). Документація Python 3.4.

Як бачите, визначення лише стоп повертає один елемент. Оскільки початкові настройки за замовчуванням є жодними, це перетворюється на отримання лише одного елемента.

Важливо зауважити, перший елемент - індекс 0, а не індекс 1. Ось чому для цієї вправи ми використовуємо 2 списки. Елементи списку А нумеруються відповідно до порядкового положення (перший елемент - 1, другий - 2 і т.д.), тоді як елементи списку В - це числа, які будуть використані для їх індексації ([0] для першого елемента 0, тощо).

За допомогою розширеного синтаксису індексації ми отримуємо діапазон значень. Наприклад, усі значення отримані двокрапкою.

A[:]

Щоб отримати підмножину елементів, потрібно визначити положення старту та зупинки.

З огляду на шаблон aList [start: stop], отримайте перші два елементи зі списку A.


3

Я не думаю, що схема підручника 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  
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.