Make an object that behaves like a slice
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 slice
s. 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).
I'M SORRY FOR THE DARK MAGIC
Using Forbiddenfruit and the python's builtin new
method I was able to do this:
from forbiddenfruit import curse
class MyThing(int):
def __new__(cls, *args, **kwargs):
magic_slice = slice(args[0], args[1])
curse(slice, 'otherstuff', args[2])
return magic_slice
thing = MyThing(1, 3, 'thing')
print 'hello world'[thing]
print thing.otherstuff
output:
>>> el
>>> thing
I wrote it as a challenge just because everybody said it is impossible, I would never use it on production code IT HAS SO MANY SIDE EFFECTS, you should think again on your structure and needs