制作一个行为类似于切片的对象

Make an object that behaves like a slice

我们如何让 class 在适当的时候将自己表示为一个切片?

这没有用:

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 继承也不起作用。

切片不能在您的 return 类型中,因为该方法不支持它。您可以阅读有关 __index__ special method here 的更多信息。 我只能想出一个解决方法,直接调用 class:

中的函数
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')
    print 'Hello World'[thing.__index__()]

这将 return el

TLDR:对于 listtuple.

等内置类型,无法自定义 classes 替换 slice

__index__ 方法的存在纯粹是为了提供一个 索引 ,根据定义,它是 python 中的一个整数(参见 the Data Model)。您不能使用它来将对象解析为 slice.

恐怕slice好像是python专门处理的。该接口需要一个实际的切片;提供其签名(还包括 indices 方法)是不够的。正如您所发现的,您无法继承它,因此您无法创建新类型的 slice。即使是 Cython 也不允许你继承它。


那么为什么 slice 很特别呢?很高兴你问。欢迎来到 CPython 的内部。看完后请洗手

因此 slice.rst 中描述了切片对象。注意这两个家伙:

.. 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.

现在,这实际上在 sliceobject.h 中实现为:

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

所以这里只允许 slice类型。此检查实际上用于list_subscript(和tuple subscript,...)after 尝试使用索引协议(因此在__index__切片是个坏主意)。自定义容器 class 可以自由覆盖 __getitem__ 并使用其自己的规则,但这就是 list(和 tuple,...)的做法。

现在,为什么不能subclass slice?好吧,type 实际上有一个标志表明某些东西是否可以被 subclassed。它被选中 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) 设置这个值的方式,但是有人得到这个错误的事实意味着它确实如此。这意味着你不能 subclass 它。


结束语:在记住了一些早已被遗忘的 C-(非)- 技能之后,我相当确定这不是严格意义上的优化。所有现有的检查和技巧仍然有效(至少我发现的那些)。

洗手并在互联网上四处搜索后,我发现了一些类似的 "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.

另请参阅 this thread 以了解关于非子 class 类型的简短讨论。

几乎所有替代解释器都在不同程度上复制了行为:Jython, Pyston, IronPython 和 PyPy(没有找到他们是如何做到的,但他们做到了)。

I'M SORRY FOR THE DARK MAGIC

使用 Forbiddenfruit 和 python 的内置 new 方法我能够做到这一点:

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

输出:

>>> el
>>> thing

我把它写成一个挑战只是因为每个人都说这是不可能的,我永远不会在生产代码中使用它它有很多副作用,你应该重新考虑你的结构和需求