无法覆盖Cython扩展的类的__init__

got*_*nes 6 python inheritance cython

我正在尝试子类化pysam的Tabixfile类并在实例化时添加其他属性.

class MyTabixfile(pysam.Tabixfile):

    def __init__(self, filename, mode='r', *args, **kwargs):
        super().__init__(filename, mode=mode, *args, **kwargs)
        self.x = 'foo'
Run Code Online (Sandbox Code Playgroud)

当我尝试实例化我的MyTabixfile子类时,我得到一个TypeError: object.__init__() takes no parameters:

>>> mt = MyTabixfile('actn2-oligos-forward.tsv.gz')
Traceback (most recent call last):
  File "<ipython-input-11-553015ac7d43>", line 1, in <module>
    mt = MyTabixfile('actn2-oligos-forward.tsv.gz')
  File "mytabix.py", line 4, in __init__
    super().__init__(filename, mode=mode, *args, **kwargs)
TypeError: object.__init__() takes no parameters
Run Code Online (Sandbox Code Playgroud)

我也尝试Tabixfile显式调用构造函数:

class MyTabixfile(pysam.Tabixfile):

    def __init__(self, filename, mode='r', *args, **kwargs):
        pysam.Tabixfile.__init__(self, filename, mode=mode, *args, **kwargs)
        self.x = 'foo'
Run Code Online (Sandbox Code Playgroud)

但这仍然提高了TypeError: object.__init__() takes no parameters.

这个类实际上是在Cython中实现的; 构造函数代码如下:

cdef class Tabixfile:
    '''*(filename, mode='r')*

    opens a :term:`tabix file` for reading. A missing
    index (*filename* + ".tbi") will raise an exception.
    '''
    def __cinit__(self, filename, mode = 'r', *args, **kwargs ):
        self.tabixfile = NULL
        self._open( filename, mode, *args, **kwargs )
Run Code Online (Sandbox Code Playgroud)

我阅读了Cython文档__cinit__并说明了__init__这一点

传递给构造函数的任何参数都将传递给 __cinit__()方法和__init__()方法.如果您预计在Python中对扩展类型进行子类化,您可能会发现给出__cinit__()方法***参数以便它可以接受并忽略额外的参数很有用.否则,具有__init__()不同签名的任何Python子类都必须重写__new__() 1以及__init__()Python类的编写者不希望必须执行的操作.

该pysam开发商并采取小心加*args**kwargsTabixfile.__cinit__方式,我的子类__init__的签名相匹配__cinit__,所以我不明白为什么我不能重写的初始化Tabixfile.

我正在使用Python 3.3.1,Cython v.0.19.1和pysam v.0.7.5进行开发.

aba*_*ert 17

这里的文档有点令人困惑,因为它假设您熟悉使用__new____init__.

__cinit__方法大致相当于__new__Python中的方法.*

__new__,__cinit__不是你叫super().__init__; 在Python甚至到达你的子类的__init__方法之前调用它.之所以__cinit__需要处理的子类的签名__init__方法是完全一样的道理__new__一样.

如果您的子类显式调用super().__init__,那么__init__再次查找超类中的方法,例如__new__,a __cinit__不是__init__.所以,除非你定义了一个__init__,否则它会传递给object.


您可以使用以下代码查看序列.

cinit.pyx:

cdef class Foo:
    def __cinit__(self, a, b, *args, **kw):
        print('Foo.cinit', a, b, args, kw)
    def __init__(self, *args, **kw):
        print('Foo.init', args, kw)
Run Code Online (Sandbox Code Playgroud)

init.py:

import pyximport; pyximport.install()
import cinit

class Bar(cinit.Foo):
    def __new__(cls, *args, **kw):
        print('Bar.new', args, kw)
        return super().__new__(cls, *args, **kw)
    def __init__(self, a, b, c, d):
        print('Bar.init', a, b, c, d)
        super().__init__(a, b, c, d)

b = Bar(1, 2, 3, 4)
Run Code Online (Sandbox Code Playgroud)

运行时,你会看到类似的东西:

Bar.new (1, 2, 3, 4) {}
Foo.cinit 1 2 (3, 4) {}
Bar.init 1 2 3 4
Foo.init (1, 2, 3, 4) {}
Run Code Online (Sandbox Code Playgroud)

所以,这里正确的解决方案取决于你想要做什么,但它是以下之一:

  1. __init__方法添加到Cython基类.
  2. super().__init__完全取消通话.
  3. 更改super().__init__为不通过任何参数.
  4. __new__向Python子类添加适当的方法.

我怀疑在这种情况下它是你想要的#2.


*值得一提的是__cinit__绝对不一样__new__.cls你得到一个部分构造的self对象(你可以信任__class__和C属性,但不是Python属性或方法),而不是获取一个参数,__new__MRO中所有类的方法都已经被调用了__cinit__; 在__cinit__你的基地被自动调用,而不是手动; 除了被请求的对象之外,你不会返回另一个对象; 它只是在它之前被调用__init__,并且预期采用传递参数,就像__new__它一样.