我正在尝试子类化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__
说了
传递给构造函数的任何参数都将传递给
__cinit__()
方法和__init__()
方法。 如果你预料到的话 在Python中继承您的扩展类型,您可能会发现它很有用 提供__cinit__()
方法*
和**
参数以便它可以 接受并忽略额外的参数。否则,任何Python子类 具有__init__()
具有不同签名的必须具有 覆盖__new__()
1以及__init__()
,作者的作者 一个Python类不希望这样做。
pysam开发人员确实谨慎地将*args
和**kwargs
添加到Tabixfile.__cinit__
方法,和我的子类__init__
与签名匹配__cinit__
的{{1}}所以我不明白为什么我无法覆盖Tabixfile
的初始化。
我正在使用Python 3.3.1,Cython v.0.19.1和pysam v.0.7.5进行开发。
答案 0 :(得分:17)
这里的文档有点令人困惑,因为它假定您熟悉使用__new__
和__init__
。
__cinit__
方法大致相当于Python中的__new__
方法。*
与__new__
一样,__cinit__
<{1}}在Python甚至到达你的子类的super().__init__
方法之前调用它。 __init__
需要处理子类__cinit__
方法签名的原因与__init__
完全相同。
如果您的子类明确调用__new__
,那么再次在超类中查找super().__init__
方法,如__init__
,__new__
不是{{1} }}。因此,除非您还定义了__cinit__
,否则它将传递到__init__
。
您可以使用以下代码查看序列。
cinit.pyx:
__init__
init.py:
object
运行时,您会看到类似的内容:
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)
方法添加到Cython基类。Bar.new (1, 2, 3, 4) {}
Foo.cinit 1 2 (3, 4) {}
Bar.init 1 2 3 4
Foo.init (1, 2, 3, 4) {}
来电。__init__
更改为不传递任何参数。super().__init__
方法。我怀疑在这种情况下它是你想要的#2。
*值得注意的是,super().__init__
肯定与<{1}}不是相同。您可以获得部分构造的__new__
对象(您可以信任__cinit__
和C属性但不信任Python属性或方法),__new__
,而不是获取cls
参数。 MRO中所有类的方法已经在任何self
之前被调用;您的基地的__class__
会自动调用而不是手动调用;除了被请求的对象之外,你不会返回另一个对象;只是它在__new__
之前被调用,并且期望采用传递参数,与__cinit__
的方式相同。
答案 1 :(得分:1)
我会评论而不是发布答案,但我还没有足够的StackOverflow foo。
@ abarnert的帖子非常好,非常有帮助。我只是在这里添加一些pysam细节,因为我刚刚以非常类似的方式对pysam.AlignmentFile进行了子类化。
选项#4是最干净/最简单的选择,它只意味着我自己的子类__new__中的更改来过滤掉未知的参数:
def __new__(cls, file_path, mode, label=None, identifier=None, *args, **kwargs):
# Suck up label and identifier unknown to pysam.AlignmentFile.__cinit__
return super().__new__(cls, file_path, mode, *args, **kwargs)
还应该注意,pysam文件类似乎没有明确的__init__方法,所以你还需要省略param pass through,因为它直接转向对象.__ init__,它不接受参数:
def __init__(self, label=None, identifier=None, *args, **kwargs):
# Handle subclass params/attrs here
# pysam.AlignmentFile doesn't have an __init__ so passes straight through to
# object which doesn't take params. __cinit__ via new takes care of params
super(pysam.AlignmentFile, self).__init__()