The __getitem__() method will receive a slice object when the object is sliced. Simply look at the start, stop, and step members of the slice object in order to get the components for the slice.
>>> class C(object):
... def __getitem__(self, val):
... print val
...
>>> c = C()
>>> c[3]
3
>>> c[3:4]
slice(3, 4, None)
>>> c[3:4:-2]
slice(3, 4, -2)
>>> c[():1j:'a']
slice((), 1j, 'a')
Answer from Ignacio Vazquez-Abrams on Stack Overflowpython - Implementing slicing in __getitem__ - Stack Overflow
How can I use Python built-in slice object? - Stack Overflow
What does the slice() function do in Python? - Stack Overflow
A rectangular jigsaw puzzle generator made with python. Criticism and suggestions are welcome.
The __getitem__() method will receive a slice object when the object is sliced. Simply look at the start, stop, and step members of the slice object in order to get the components for the slice.
>>> class C(object):
... def __getitem__(self, val):
... print val
...
>>> c = C()
>>> c[3]
3
>>> c[3:4]
slice(3, 4, None)
>>> c[3:4:-2]
slice(3, 4, -2)
>>> c[():1j:'a']
slice((), 1j, 'a')
I have a "synthetic" list (one where the data is larger than you would want to create in memory) and my __getitem__ looks like this:
def __getitem__(self, key):
if isinstance(key, slice):
# Get the start, stop, and step from the slice
return [self[ii] for ii in xrange(*key.indices(len(self)))]
elif isinstance(key, int):
if key < 0: # Handle negative indices
key += len(self)
if key < 0 or key >= len(self):
raise IndexError, "The index (%d) is out of range." % key
return self.getData(key) # Get the data from elsewhere
else:
raise TypeError, "Invalid argument type."
The slice doesn't return the same type, which is a no-no, but it works for me.
You create a slice by calling slice with the same fields you would use if doing [start:end:step] notation:
Use with AIsl = slice(0,4)
To use the slice, just pass it as if it were the index into a list or string:
Use with AI>>> s = "ABCDEFGHIJKL"
>>> sl = slice(0,4)
>>> print(s[sl])
'ABCD'
Let's say you have a file of fixed-length text fields. You could define a list of slices to easily extract the values from each "record" in this file.
Use with AIdata = """\
0010GEORGE JETSON 12345 SPACESHIP ST HOUSTON TX
0020WILE E COYOTE 312 ACME BLVD TUCSON AZ
0030FRED FLINTSTONE 246 GRANITE LANE BEDROCK CA
0040JONNY QUEST 31416 SCIENCE AVE PALO ALTO CA""".splitlines()
fieldslices = [slice(*fielddef) for fielddef in [
(0,4), (4, 21), (21,42), (42,56), (56,58),
]]
fields = "id name address city state".split()
for rec in data:
for field,sl in zip(fields, fieldslices):
print("{} : {}".format(field, rec[sl]))
print('')
# or this same code using itemgetter, to make a function that
# extracts all slices from a string into a tuple of values
import operator
rec_reader = operator.itemgetter(*fieldslices)
for rec in data:
for field, field_value in zip(fields, rec_reader(rec)):
print("{} : {}".format(field, field_value))
print('')
Prints:
Use with AIid : 0010
name : GEORGE JETSON
address : 12345 SPACESHIP ST
city : HOUSTON
state : TX
id : 0020
name : WILE E COYOTE
address : 312 ACME BLVD
city : TUCSON
state : AZ
id : 0030
name : FRED FLINTSTONE
address : 246 GRANITE LANE
city : BEDROCK
state : CA
id : 0040
name : JONNY QUEST
address : 31416 SCIENCE AVE
city : PALO ALTO
state : CA
Square brackets following a sequence denote either indexing or slicing depending on what's inside the brackets:
Use with AI>>> "Python rocks"[1] # index
'y'
>>> "Python rocks"[1:10:2] # slice
'yhnrc'
Both of these cases are handled by the __getitem__() method of the sequence (or __setitem__() if on the left of an equals sign.) The index or slice is passed to the methods as a single argument, and the way Python does this is by converting the slice notation, (1:10:2, in this case) to a slice object: slice(1,10,2).
So if you are defining your own sequence-like class or overriding the __getitem__ or __setitem__ or __delitem__ methods of another class, you need to test the index argument to determine if it is an int or a slice, and process accordingly:
Use with AIdef __getitem__(self, index):
if isinstance(index, int):
... # process index as an integer
elif isinstance(index, slice):
start, stop, step = index.indices(len(self)) # index is a slice
... # process slice
else:
raise TypeError("index must be int or slice")
A slice object has three attributes: start, stop and step, and one method: indices, which takes a single argument, the length of the object, and returns a 3-tuple: (start, stop, step).
a[x:y:z] gives the same result as a[slice(x, y, z)]. One of the advantages of a slice object is that it can be stored and retrieved later as a single object instead of storing x, y and z.
It is often used to let the user define their own slice that can later be applied on data, without the need of dealing with many different cases.
(Using function semantics) Calling the slice class instantiates a slice object (start,stop,step), which you can use as a slice specifier later in your program:
>>> myname='Rufus'
>>> myname[::-1] # reversing idiom
'sufuR'
>>> reversing_slice=slice(None,None,-1) # reversing idiom as slice object
>>> myname[reversing_slice]
'sufuR'
>>> odds=slice(0,None,2) # another example
>>> myname[odds]
'Rfs'
If you had a slice you often used, this is preferable to using constants in multiple program areas, and save the pain of keeping 2 or 3 references that had to be typed in each time.
Of course, it does make it look like an index, but after using Python a while, you learn that everything is not what it looks like at first glance, so I recommend naming your variables better (as I did with reversing_slice, versus odds which isn't so clear.