Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
265 views
in Technique[技术] by (71.8m points)

python - Make an object that behaves like a slice

How can we make a class represent itself as a slice when appropriate?

This didn't work:

class MyThing(object):
    def __init__(self, start, stop, otherstuff):
        self.start = start
        self.stop = stop
        self.otherstuff = otherstuff
    def __index__(self):
        return slice(self.start, self.stop)

Expected output:

>>> thing = MyThing(1, 3, 'potato')
>>> 'hello world'[thing]
'el'

Actual output:

TypeError: __index__ returned non-(int,long) (type slice)

Inheriting from slice doesn't work either.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

TLDR: It's impossible to make custom classes replace slice for builtins types such as list and tuple.


The __index__ method exists purely to provide an index, which is by definition an integer in python (see the Data Model). You cannot use it for resolving an object to a slice.

I'm afraid that slice seems to be handled specially by python. The interface requires an actual slice; providing its signature (which also includes the indices method) is not sufficient. As you've found out, you cannot inherit from it, so you cannot create new types of slices. Even Cython will not allow you to inherit from it.


So why is slice special? Glad you asked. Welcome to the innards of CPython. Please wash your hands after reading this.

So slice objects are described in slice.rst. Note these two guys:

.. c:var:: PyTypeObject PySlice_Type

The type object for slice objects. This is the same as :class:slice in the Python layer.

.. c:function:: int PySlice_Check(PyObject *ob) Return true if ob is a slice object; ob must not be NULL.

Now, this is actually implemented in sliceobject.h as :

#define PySlice_Check(op) (Py_TYPE(op) == &PySlice_Type)

So only the slice type is allowed here. This check is actually used in list_subscript (and tuple subscript, ...) after attempting to use the index protocol (so having __index__ on a slice is a bad idea). A custom container class is free to overwrite __getitem__ and use its own rules, but that's how list (and tuple, ...) does it.

Now, why is it not possible to subclass slice? Well, type actually has a flag indicating whether something can be subclassed. It is checked here and generates the error you have seen:

    if (!PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) {
        PyErr_Format(PyExc_TypeError,
                     "type '%.100s' is not an acceptable base type",
                     base_i->tp_name);
        return NULL;
    }

I haven't been able to track down how slice (un)sets this value, but the fact that one gets this error means it does. This means you cannot subclass it.


Closing remarks: After remembering some long-forgotten C-(non)-skills, I'm fairly sure this is not about optimization in the strict sense. All existing checks and tricks would still work (at least those I've found).

After washing my hands and digging around in the internet, I've found a few references to similar "issues". Tim Peters has said all there is to say:

Nothing implemented in C is subclassable unless somebody volunteers the work to make it subclassable; nobody volunteered the work to make the [insert name here] type subclassable. It sure wasn't at the top of my list wink.

Also see this thread for a short discussion on non-subclass'able types.

Practically all alternative interpreters replicate the behavior to various degrees: Jython, Pyston, IronPython and PyPy (didn't find out how they do it, but they do).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...