gpt4 book ai didi

python - 无法从 Cython 扩展覆盖类的 __init__

转载 作者:太空狗 更新时间:2023-10-29 22:04:55 31 4
gpt4 key购买 nike

我正在尝试子类化 pysam's Tabixfile类并在实例化时添加额外的属性。

class MyTabixfile(pysam.Tabixfile):

def __init__(self, filename, mode='r', *args, **kwargs):
super().__init__(filename, mode=mode, *args, **kwargs)
self.x = 'foo'

当我尝试实例化我的 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

我还尝试显式调用 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'

但这仍然引发 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 )

我通读了 Cython documentation on __cinit__ and __init__这说

Any arguments passed to the constructor will be passed to both the __cinit__() method and the __init__() method. If you anticipate subclassing your extension type in Python, you may find it useful to give the __cinit__() method * and ** arguments so that it can accept and ignore extra arguments. Otherwise, any Python subclass which has an __init__() with a different signature will have to override __new__() 1 as well as __init__(), which the writer of a Python class wouldn’t expect to have to do.

pysam 开发人员确实注意将 *args**kwargs 添加到 Tabixfile.__cinit__ 方法, 我的子类 __init__ 匹配 __cinit__ 的签名,所以我不明白为什么我无法覆盖 Tabixfile 的初始化。

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

最佳答案

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

__cinit__ 方法大致等同于 Python 中的 __new__ 方法。*

__new__ 一样,__cinit__ 由您的 super().__init__ 调用;它在 Python 到达子类的 __init__ 方法之前被调用。 __cinit__ 需要处理子类 __init__ 方法的签名的原因与 __new__ 的原因完全相同。

如果您的子类确实显式调用了 super().__init__,它会在父类(super class)中查找 __init__ 方法——同样,如 __new__ , __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)

初始化文件:

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)

运行时,您会看到如下内容:

Bar.new (1, 2, 3, 4) {}
Foo.cinit 1 2 (3, 4) {}
Bar.init 1 2 3 4
Foo.init (1, 2, 3, 4) {}

因此,此处正确的修复取决于您要执行的操作,但它是其中之一:

  1. 向 Cython 基类添加一个 __init__ 方法。
  2. 完全删除 super().__init__ 调用。
  3. 更改 super().__init__ 以不传递任何参数。
  4. 向 Python 子类添加适当的 __new__ 方法。

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


* 值得注意的是 __cinit____new__ 绝对相同。你得到的不是 cls 参数,而是部分构造的 self 对象(你可以信任 __class__ 和 C 属性,但不是 Python 属性或methods),MRO中所有类的__new__方法在任何__cinit__之前已经被调用;你的基地的 __cinit__ 被自动调用而不是手动调用;除了被请求的对象之外,您不会返回其他对象;等等。只是它在 __init__ 之前被调用,并且期望以与 __new__ 相同的方式获取传递参数。

关于python - 无法从 Cython 扩展覆盖类的 __init__,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18260095/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com