Update (2023-05-12)
I opened a PR to make slice generic. The change would introduce quite a few mypy errors in many existing projects. The good people at python/typeshed made it clear that until we have type variable defaults (proposed in PEP 696, but not yet accepted) and the slice type utilizes them, they will not accept the change to make it generic.
I will try to keep up to date on the progress with PEP 696 and to implement the missing changes as soon as possible.
Original answer
As it stands right now, the built-in slice type is not generic, so it has no type parameters (unlike e.g. list). There has been a long-standing discussion about this (since 2015) and the relevant typeshed issue is still open. IMO it should definitely be made generic, but it is unclear if/when that will happen.
slice arguments can be of any type (source). This means a statement like e.g. slice("foo", [-1], object()) will pass mypy --strict (even though it may be of questionable utility).
That means there is currently no way to constrain a slice type variable in terms of its start/stop/step attribute types.
And since you cannot even subclass slice, there is not even a way to easily make your own generic slice-like type.
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).