从元组子类

时间:2017-05-12 16:54:50

标签: python python-3.x inheritance tuples

我正在使用第三方模块,该模块提供从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版本,同样失败:

3.3.6

planar

2.7.11

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类特有的。很高兴知道究竟是什么导致了这个问题。

1 个答案:

答案 0 :(得分:1)

事实证明确实存在涉及错误的扩展模块。 planar根本没有使用您编辑过的planar.transform模块;它正在使用planar.cplanar的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,因此不会处理错误报告。可能有更好的模块可供使用。