我正在使用第三方模块,该模块提供从tuple
继承的类。但是,我想为这些类添加一些功能,所以我已经分类了。结果继承层次结构如下所示:
MyClass
- > LibClass
- > tuple
tuple
子类的继承是否应预期失败?
一开始似乎一切都很好看。但是,利用切片(instance[:6]
)从MyClass
的实例访问一系列值会导致如下错误:
SystemError: <method-wrapper '__getitem__' of LibClass object at 0x1010101010> returned NULL without setting an error
对LibClass
的实例做同样的事情完美无瑕。
为了进一步增加神秘感,instance[5]
实例上的常规索引访问(MyClass
)可以正常运行。
显然tuple
继承不像正常的类继承那样完全(即必须覆盖__new__
而不是__init__
)。但是,据我所知,LibClass
正在这样做,例如。
def __new__(cls, *members):
mat = [x * 1.0 for x in members] + [0.0, 0.0, 1.0]
return tuple.__new__(cls, mat)
我认为在__new__
中实现MyClass
是必要的,因为LibClass
中的实现正确地通过cls
(诚然,我必须分叉库以实现该目的) )。尽管如此,对于后人,我也尝试直接在__new__
中实施MyClass
(只是复制并粘贴了LibClass
实现)。
我还应该注意到,MyClass
我没有做任何古怪的事情。事实上,如果我什么都没有,问题仍然存在,例如。
class MyClass(lib.LibClass):
pass
另一件值得注意的事情是LibClass
没有自定义__getitem__
实现 - 它只是从tuple
继承该行为。
Python 3.6.1
LibClass
实际上是来自Affine
的{{1}},我的分叉可以在这里找到:
https://github.com/Benjamin-Dobell/planar/blob/master/lib/planar/transform.py
planar
评论中指出,上述复制pip install git+https://github.com/Benjamin-Dobell/planar#egg=planar
python
>>> import planar
>>> class Affine(planar.Affine):
... pass
...
>>> planar.Affine.identity()[:6]
(1.0, 0.0, 0.0, 0.0, 1.0, 0.0)
>>> Affine.identity()[:6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: <method-wrapper '__getitem__' of Affine object at 0x10e2b9ba8> returned NULL without setting an error
会返回一个常数。所以真的不应该失败。我无法解释。但是,我应该补充说,这是一个非常糟糕的复制品。我的真实世界用法更接近:
identity()
也以同样的方式失败。
请注意,常数的失败确实让我摸不着头脑。
>>> Affine.translation((0, 0))[:6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: <method-wrapper '__getitem__' of Affine object at 0x10f8d5ee8> returned NULL without setting an error
>>> planar.Affine.translation((0, 0))[:6]
(1.0, 0.0, 0.0, 0.0, 1.0, 0.0)
尝试不同的Python版本,同样失败:
planar
Python 3.3.6 (default, Apr 12 2017, 17:20:32)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import planar
>>> class Affine(planar.Affine):
... pass
...
>>> planar.Affine.identity()[:6]
(1.0, 0.0, 0.0, 0.0, 1.0, 0.0)
>>> Affine.identity()[:6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: NULL result without error in PyObject_Call
然而,当我简化为最简单的形式时,我无法重现问题,即(Python 2.7.11):
Python 2.7.11 (default, May 2 2016, 14:38:51)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import planar
>>> class Affine(planar.Affine):
... pass
...
>>> planar.Affine.identity()[:6]
(1.0, 0.0, 0.0, 0.0, 1.0, 0.0)
>>> Affine.identity()[:6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: NULL result without error in PyObject_Call
我还尝试将>>> class LibClass(tuple):
... def __new__(cls, *members):
... return tuple.__new__(cls, *members)
...
>>> class MyClass(LibClass):
... pass
...
>>> LibClass((1, 2, 3, 4, 5))[:3]
(1, 2, 3)
>>> MyClass((1, 2, 3, 4, 5))[:3]
(1, 2, 3)
的定义移到单独的LibClass
,以确保错误与Python模块没有任何关系,但它的工作原理如上所述。
因此问题是lib.py
和/或其planar
类特有的。很高兴知道究竟是什么导致了这个问题。
答案 0 :(得分:1)
事实证明确实存在涉及错误的扩展模块。 planar
根本没有使用您编辑过的planar.transform
模块;它正在使用planar.c
,planar
的C实现具有自己的Affine
类。
问题的至少一部分似乎是由于Affine_getitem
中的错误:
static PyObject *
Affine_getitem(PlanarAffineObject *self, Py_ssize_t i)
{
double m;
assert(PlanarAffine_Check(self));
if (i < 6) {
m = self->m[i];
} else if (i < 8) {
m = 0.0;
} else if (i == 8) {
m = 1.0;
} else {
return NULL;
}
return PyFloat_FromDouble(m);
}
如果没有为超出范围的索引设置NULL
,则返回IndexError
。
planar
,因此不会处理错误报告。可能有更好的模块可供使用。