Питання Розуміння позначення скибочками Python


Мені потрібне хороше пояснення (посилання - це плюс) на позначенні фрагмента Python.

Для мене ця нотатка потребує трохи підбору.

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


2300
2018-02-03 22:31


походження




Відповіді:


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

a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[:]         # a copy of the whole array

Існує також step вартість, яка може бути використана з будь-яким з перерахованих вище:

a[start:end:step] # start through not past end, by step

Ключовим моментом, який слід пам'ятати, є те, що :end Значення являє собою перше значення, яке є ні у вибраному фрагменті. Отже, різниця між ними end і start це кількість вибраних елементів (якщо step 1, за замовчуванням).

Інша особливість - це start або end може бути a негативний номер, що означає, що він починається з кінця масиву. Так:

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


3111
2018-02-03 22:48



Вирізати вбудовані типи повертає копію, але це не є універсальним. Примітно, що нарізання NumPy масивів повертає представлення, яке обмінюється пам'яттю з оригіналом. - Beni Cherniavsky-Paskin
Що б це зробити? [::-2]  Я спробував це, але я не розумію цього. Наприклад, з цим списком a = ['1', '2', '3', '4', '5'] . - RodriKing


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

Діаграма мистецтва ASCII також корисна для того, щоб запам'ятати, як працюють фрагменти:

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

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


395
2018-02-03 22:49



Для фрагментів з негативними кроками, я вважаю це ASCII мистецтво заплутаним, і це повинно поширюватися на -6 з 'ApleH'[:-6:-1] є дійсним фрагментом і відрізняється від використання -5 - Chris_Rands
"Один із способів згадати, як працюють фрагменти, - це думати про індекси як про те, що вказує на символи" - це прекрасний спосіб думати про це - jusopi


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

>>> 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 `item`
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'

310
2018-02-03 23:08



щось незвичайне буває, якщо stride є негативним, як у [::-1]? - Charlie Parker
@CharlieParker коли strike є негативним, він відраховує від high до low. - ephemient
Насправді все ще є щось залишене, наприклад, якщо я ввожу "Apple" [4: -4: -1], я отримую "elp", python переводить -4 на 1, може бути? - liyuan


Наведені вище відповіді не обговорюють присвоєння шматочків:

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

Це також може прояснити різницю між нарізанням та індексацією.


199
2018-01-18 21:37



ти можеш пояснити другий? - chandresh


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

Одним словом, двокрапками (:) в нотації підписки (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]

The кишка, :, це те, що Python повідомляє, що ви надаєте йому шматок, а не звичайний індекс. Ось чому ідіоматичним способом створення неглибокої копії списків в Python 2 є

list_copy = sequence[:]

І очищення їх з:

del my_list[:]

(Python 3 отримує a list.copy і list.clear метод.)

Коли step є негативним, за замовчуванням для start і stop змінити

За замовчуванням, коли step аргумент порожній (або None), йому призначено +1.

Але ви можете передати у від'ємне ціле число, і список (або більшість інших стандартних шрежиків) буде нарізаний від кінця до початку.

Таким чином, негативний фрагмент змінить значення за замовчуванням start і stop!

Підтвердження цього в джерелі

Я люблю закликати користувачів прочитати джерело, а також документацію. The Вихідний код для фрагментів об'єктів, і ця логіка знаходиться тут. Спочатку визначимо чи step є негативним:

 step_is_negative = step_sign < 0;

Якщо так, то нижня межа -1  а це означає, що ми відрізаємо весь шлях до і включаючи початок, а верхня межа - довжина мінус 1, тобто ми починаємо в кінці. (Зверніть увагу, що семантика цього -1 є інший від a -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, він може повернути вигляд оригіналу, а не копію.


186
2017-07-12 13:19



Привіт @araonhall, відмінний пост! Чи хотіли б ви додати цю публікацію до проміжної книги Python - github.com/yasoob/intermediatePython/issues/153 ? - neowulf33
Просто хотів сказати вам багато спасибі за цей пост, я пишу Python на деякий час і все ще повертаюся до цього, тому що я розбиваю по одній помилці весь час з нарізанням! - akkatracker
найкраща відповідь тут - kskyriacou


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

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

124
2018-02-03 23:15





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

84
2017-09-06 06:50





У 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 (вперед або назад), починайте з і закінчується на 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]

84
2017-10-22 05:33



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


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

(from:to:step)

будь-який з них необов'язковий

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

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

Усе це працює для мене ...


47
2018-02-19 20: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().)

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

ЗРОБИТИ: У наведеному нижче коді було помилка з "ніколи не виходити з послідовності", коли abs (step)> 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] це всього лише рядок.


32
2018-03-29 10:15



The this_is_how_slicing_works не така ж, як пітона. Е.Г. [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. Я перероблю це пізніше сьогодні (з тестами). - Beni Cherniavsky-Paskin