Разбиране на нотацията за парчета

Имам нужда от добро обяснение (препратките са предимство) на Python'slice notation.

Според мен тази нотация се нуждае от малко усвояване.

Изглежда изключително мощна, но аз не съм се ориентирал в нея.

Решение

Всъщност е много просто:

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

По същия начин стъпка може да бъде отрицателно число:

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() опростява програмното генериране на нарязването.

Коментари (7)

В Python tutorial се говори за това (превъртете малко надолу, докато стигнете до частта за нарязването).

Схемата на ASCII изкуството също е полезна за запомняне на начина на работа с резени:

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

Един от начините да запомните как работят парчетата е да си представите индексите като сочещи между символите, като левият край на първия символ е с номер 0. Тогава десният край на последния символ от низ от n символа има индекс n.

Коментари (2)

Изброяване на възможностите, разрешени от граматиката:

>>> 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)'
Коментари (3)