创建一个行为像切片的对象

时间:2016-10-11 05:33:04

标签: python slice

我们怎样才能让一个类在适当时将自己表示为切片?

这没有用:

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

3 个答案:

答案 0 :(得分:7)

TLDR:对于内置类型(例如slicelist),无法使自定义类替换tuple

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

我担心slice似乎是由python专门处理的。界面需要一个实际的切片;提供其签名(也包括indices方法)是不够的。正如您所知,您无法继承它,因此您无法创建新类型的slice。即使Cython也不允许你继承它。

为什么slice特别?很高兴你问。欢迎来到CPython的内部。阅读本文后请洗手。

所以切片对象在slice.rst中描述。请注意这两个人:

  

.. c:var :: PyTypeObject PySlice_Type

     

切片对象的类型对象。这与:class:slice中的相同      Python层。

     

.. c:function :: int PySlice_Check(PyObject * ob)      如果 ob 是切片对象,则返回true; ob 不得 NULL

现在,这实际上是在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(联合国)如何设置此值,但是获得此错误的事实意味着它确实如此。这意味着你不能将它子类化。

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

在洗手并在互联网上挖掘之后,我发现了一些类似的“问题”。 Tim Peters has said all there is to say:

  

除非有人自愿完成工作,否则C中没有任何实现是可继承的   使其成为可继承的;没有人自愿完成 [在此插入名称] 的工作   type subclassable。它肯定不在我的列表顶部 wink

另见this thread关于非子类可用类型的简短讨论。

实际上,所有替代解释器都会将行为复制到不同程度:JythonPystonIronPython和PyPy(没有发现他们是如何做到的,但他们确实如此)。< / p>

答案 1 :(得分:5)

  
    

我很抱歉黑魔法

  

使用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

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

答案 2 :(得分:-2)

切片无法进入return类型,因为该方法不支持此功能。您可以详细了解__index__ special method here。 我只能想出一个直接调用你班级函数的解决方法:

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__()]

这将返回el