Why is floating point slicing (slice(0,1,0.1)) allowed in python, but calling the indices method (slice(0,1,0.1).indices) raises TypeError?

FirefoxMetzger :

Example: slice(0,1,0.1) is okay in python 3.7.7, but slice(0,1,0.1).indices(10) raises a Type Error.

I am wondering what the underlying thought process for this behavior is. Why is it okay to construct float slices, but not okay to call the indices method on them? What is a use case for indices?

For context: I understand the use case for float slices - I have one myself. I am implementing an object that represents a parameterized, piece wise linear function in arbitrary dimensions defined by a finite set of supporting points.

Green Cloak Guy :

tl;dr slice objects are generated by the interpreter when we use them in square-bracket notation, and allowing them to be arbitrary allows us to design code that uses them. However, because the built-in list relies on indices() to behave properly, that method has to return list-compatible values (i.e. integers), and if it can't, it throws an error.


When you do

my_obj[1:3:2]

the interpreter essentially translates it* as

my_obj.__getitem__(slice(1, 3, 2))

This is most obvious when using lists, which have special behavior when slices are given, but this behavior is also other datatypes in various popular libraries (e.g. numpy.array and pandas.Dataframe). These classes implement their own __getitem__() methods, that have their own special ways to handle slices.

Now, the built-in list presumably uses slice.indices() to decompose the entire slice into a set of individual indices that it can access and then group together and return. List indices can only be integers, and they don't want this functionality to break, so the most consistent way to go about it is to make slice.indices() throw an error when it can't produce a list of integers.

They can't restrict slice to having only those values, though, because it's an interpreter-generated object that other user-defined classes might want to use. If you design an object like this:

class myGenerator:
    def __getitem__(self, s):  # s is a slice
        def gen():
            i = s.start
            while i < s.stop:
                yield i
                i += s.step
        return list(gen())

h = myGenerator()
print(h[1:4:.25])
# [1, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75]
print(h[0:1:0.1])
# [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.8999999999999999, 0.9999999999999999]

then it can co-opt slice notation to work however it wants, so we can institute custom behavior for it. But if we were to change slice.indices() to use that instead, then it would break the built-in list - thus, python doesn't allow us to.


*technically, for a lot of built-ins, the python interpreter may take shortcuts and executes hardcoded routines instead of actually translating the notation into function calls and executing them. But for our purposes the analogy works well enough, since it does do that for user-generated objects.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=407676&siteId=1