创建行为类似于切片的对象
问题描述
如何在适当的时候使类将其自身表示为切片?
这不起作用:
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)
预期输出:
>>> thing = MyThing(1, 3, 'potato')
>>> 'hello world'[thing]
'el'
实际产量:
TypeError: __index__ returned non-(int,long) (type slice)
从slice
继承也不起作用。
解决方案
TLDR:无法使自定义类替换list
和tuple
等内置类型的slice
。
__index__
方法的存在纯粹是为了提供一个索引,根据定义,它是python中的一个整数(请参见the Data Model)。不能使用它将对象解析为slice
。
恐怕slice
似乎是由python专门处理的。该接口需要实际的片;仅提供其签名(还包括indices
方法)是不够的。正如您已经发现的,您不能从它继承,因此您不能创建新的slice
类型。即使是Cython也不允许您从它继承。
那么slice
为什么特别呢?很高兴你这么问。欢迎来到CPython的内部。请在阅读本文后洗手。
因此切片对象在slice.rst
中描述。注意这两个家伙:
..C:var::PyTypeObject PySlice_Type
切片对象的类型对象。这与:类:slice
中的 Python层。..C:Function::Int PySlice_Check(PyObject*ob) 如果ob是切片对象,则返回True;ob不能为空。
现在,这实际上在sliceobject.h
中实现为:
#define PySlice_Check(op) (Py_TYPE(op) == &PySlice_Type)
因此此处仅允许slice
类型。此检查实际上是在尝试使用索引协议之后的list_subscript
(和tuple subscript
,...)中使用的(因此将__index__
放在片上不是一个好主意)。自定义容器类可以自由覆盖__getitem__
并使用自己的规则,但这就是list
(和tuple
,...)做到了。
slice
呢?嗯,type
实际上有一个标志,指示某个东西是否可以子类化。它被选中here并生成您看到的错误:
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;
}
我无法跟踪slice
(Un)如何设置此值,但收到此错误的事实意味着它确实设置了该值。这意味着您不能将其子类。
结束语:在记住一些被遗忘已久的C-(非)-技能之后,我相当确定这不是严格意义上的优化。所有现有检查和技巧仍然有效(至少我找到的那些)。
在洗手和在互联网上翻找之后,我找到了一些关于类似"问题"的参考资料。Tim Peters has said all there is to say:
除非有人自愿执行工作,否则用C实现的任何东西都不是可子类的 使其可子类;没有人自愿将[在此处插入名称] 类型Subclassable。它肯定不在我列表的首位眨眼。
另请参阅this thread,了解有关不可子类类型的简短讨论。
几乎所有替代解释器都不同程度地复制了该行为:Jython、Pyston、IronPython和PyPy(没有了解它们是如何做到的,但它们确实做到了)。
相关文章