制作一个表现如同一个切片的物体
如何在合适的情况下让一个班级将自己表现为切片?制作一个表现如同一个切片的物体
这不起作用:
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:使用自定义类替换slice
的内置类型(如list
和tuple
)是不可能的。
的__index__
方法纯粹的存在是为了提供一种索引,其是由定义在python的整数(参见the Data Model)。您无法使用它将对象解析为slice
。
恐怕slice
似乎是由python专门处理。界面需要一个实际的切片;提供其签名(其中还包括indices
方法)是不够的。正如你发现的那样,你不能继承它,所以你不能创建新类型的slice
。即使Cython也不会让你继承它。
那么为什么slice
特别?很高兴你问。欢迎来到CPython的内部。阅读后请洗手。
所以切片对象在slice.rst
中描述。请注意这两个家伙:
。C:VAR :: PyTypeObject PySlice_Type
的切片对象的对象类型。这与Python层中的
slice
类中的类: 相同。.. 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
(UN)如何设置这个值,而是一个得到这个错误的事实意味着它。这意味着你不能继承它。
结束语:记住一些长期被遗忘的C-(非)-skills后,我相当肯定这是不是严格意义上的优化。所有现有的检查和技巧仍然有效(至少是我发现的)。
在洗完手和在互联网上四处挖掘之后,我发现了一些类似“问题”的参考。 Tim Peters has said all there is to say:
用C语言实现没有什么是子类化,除非有人志愿者工作 使它子类化;没有人自告奋勇地将[插入名称] 类型子分类。它肯定不在我的名单的顶部wink。
另请参阅this thread关于非子类类型的简短讨论。
几乎所有的替代口译复制行为在不同程度:Jython,Pyston,IronPython和PyPy(没搞清楚他们是如何做到这一点,但是他们这样做)。
您能否提供一个需要实际切片的接口的参考,并且以某种方式欺骗它与子类是不可能的? “看起来你做不到”这不是一个特别令人信服的答案。 – wim
我不是说你好像不能这样做。我说你必须提供一个切片,而且你不能创建新的切片类型。当你无法创建子类时,用子类来诱惑它是不可能的。也就是说,我正在查看源代码,看看是否有确切的东西出现。 – MisterMiyagi
这似乎违反了鸭子打字。如果'__getitem__'接受一个切片对象(它确实),它应该可以接受一些像切片一样的东西。 – wim
切片不能在您的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
。
不,它被左侧的'__getitem__'隐式调用。你的建议等同于定义像as_slice(self):return slice(self.start,self.stop)这样的'as_slice'方法,这是我意识到的解决方法,但不是我想要的。 – wim
如果你已经阅读了允许任何对象用于切片的PEP 357,你会知道'__index__' *的返回类型具有*为'int'或'long'。切片不能在那里工作。 – Mangohero1
调用'__index__'有什么用?他可以通过一个切片 - 无论哪种方式,它不会是自定义类。 – MisterMiyagi
我很抱歉黑暗魔法
使用Forbiddenfruit和Python 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
我写的是一个挑战,只是因为大家都说这是不可能的,我永远不会使用它的生产代码它有许多副作用,你应该重新考虑你的结构和需求
哦,是的,monkeypatching。嗯。不幸的是,这种黑客行为并不适用于我想要使用的方法('__or__','__and__'等),但无论如何都有提示。 – wim
这正在改变切片,而不是创建一个新的类。之后可以做'slice(2,3,4).otherstuff','type(MyThing(1,2,3))'实际上是'slice'。 “MyThing”的实际实例仍不能用作“分片”。 – MisterMiyagi
好,从它看起来像'__index __()'强调返回一个'int'而不是'slice',并且它在返回单个整数时工作正常。 –
顺便说一句,你为什么要这么做? – MisterMiyagi
我正在制作一种应该像切片一样行为的丰富切片,但也有一些扩展功能。 – wim