Begrijpen van slice notatie

Ik heb een goede uitleg nodig (referenties zijn een plus) over Python's slice notatie.

Voor mij moet deze notatie een beetje opgepikt worden.

Het ziet er zeer krachtig uit, maar ik'heb mijn hoofd er nog niet helemaal bij.

Oplossing

Het is eigenlijk heel simpel:

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

Er is ook de step waarde, die met elk van de bovenstaande kan worden gebruikt:

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

Het belangrijkste punt om te onthouden is dat de :stop waarde de eerste waarde vertegenwoordigt die niet in de geselecteerde slice zit. Dus, het verschil tussen stop en start is het aantal geselecteerde elementen (als step 1 is, de standaard).

De andere eigenschap is dat start of stop een negatief getal kan zijn, wat betekent dat het telt vanaf het einde van de array in plaats van het begin. Dus:

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

Op dezelfde manier kan step een negatief getal zijn:

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 is vriendelijk voor de programmeur als er minder items zijn dan waar je om vraagt. Als je bijvoorbeeld vraagt om a[:-2] en a bevat maar één element, dan krijg je een lege lijst in plaats van een foutmelding. Soms zou je liever de error krijgen, dus je moet je ervan bewust zijn dat dit kan gebeuren.

Relatie tot slice() object

De slicing operator [] wordt in de bovenstaande code gebruikt met een slice() object door gebruik te maken van de : notatie (die alleen geldig is binnen []), d.w.z.:

a[start:stop:step]

is gelijk aan:

a[slice(start, stop, step)]

Slice objecten gedragen zich ook iets anders afhankelijk van het aantal argumenten, net als bij range(), d.w.z. zowel slice(stop) als slice(start, stop[, step]) worden ondersteund. Om het specificeren van een bepaald argument over te slaan, kan men None gebruiken, zodat bijvoorbeeld a[start:] gelijk is aan a[slice(start, None)] of a[::-1] gelijk is aan a[slice(None, None, -1)].

Hoewel de :-gebaseerde notatie erg handig is voor eenvoudige slicing, vereenvoudigt het expliciete gebruik van slice() objecten de programmatische generatie van slicing.

Commentaren (7)

De Python tutorial heeft het erover (scroll een beetje naar beneden tot je bij het gedeelte over slicen komt).

Het ASCII art diagram is ook nuttig om te onthouden hoe slices werken:

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

Een manier om te onthouden hoe slices werken is te denken aan de indices als wijzend tussen tekens, met de linkerrand van het eerste teken genummerd 0. Dan heeft de rechterrand van het laatste teken van een string van n tekens index n.

Commentaren (2)

Een opsomming van de mogelijkheden die door de grammatica worden toegestaan:

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

Natuurlijk, als (hoog-laag)%stride != 0, dan zal het eindpunt iets lager liggen dan hoog-1.

Als stride negatief is, wordt de volgorde een beetje veranderd omdat we'naar beneden tellen:

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

Uitgebreide slicing (met komma's en ellipsen) worden meestal alleen gebruikt door speciale datastructuren (zoals NumPy); de basisreeksen ondersteunen ze niet.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
Commentaren (3)