python - Can't override __init__ of class from Cython extension -
i trying subclass pysam's tabixfile
class , add additional attributes on instantiation.
class mytabixfile(pysam.tabixfile): def __init__(self, filename, mode='r', *args, **kwargs): super().__init__(filename, mode=mode, *args, **kwargs) self.x = 'foo'
when try instantiate mytabixfile
subclass, 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
i tried calling tabixfile
constructor explicitly:
class mytabixfile(pysam.tabixfile): def __init__(self, filename, mode='r', *args, **kwargs): pysam.tabixfile.__init__(self, filename, mode=mode, *args, **kwargs) self.x = 'foo'
but still raises typeerror: object.__init__() takes no parameters
.
this class implemented in cython; constructor code below:
cdef class tabixfile: '''*(filename, mode='r')* opens :term:`tabix file` reading. missing index (*filename* + ".tbi") raise exception. ''' def __cinit__(self, filename, mode = 'r', *args, **kwargs ): self.tabixfile = null self._open( filename, mode, *args, **kwargs )
i read through cython documentation on __cinit__
, __init__
says
any arguments passed constructor passed both
__cinit__()
method ,__init__()
method. if anticipate subclassing extension type in python, may find useful give__cinit__()
method*
,**
arguments can accept , ignore arguments. otherwise, python subclass has__init__()
different signature have override__new__()
1__init__()
, writer of python class wouldn’t expect have do.
the pysam developers did take care add *args
, **kwargs
tabixfile.__cinit__
method, and subclass __init__
matches signature of __cinit__
not understand why i'm unable override initialization of tabixfile
.
i'm developing python 3.3.1, cython v.0.19.1, , pysam v.0.7.5.
the documentation little confusing here, in assumes you're familiar using __new__
, __init__
.
the __cinit__
method equivalent __new__
method in python.*
like __new__
, __cinit__
not called super().__init__
; it's called before python gets subclass's __init__
method. reason __cinit__
needs handle signature of subclass __init__
methods exact same reason __new__
does.
if subclass explicitly call super().__init__
, looks __init__
method in superclass—again, __new__
, __cinit__
not __init__
. so, unless you've also defined __init__
, pass through object
.
you can see sequence following code.
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)
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)
when run, you'll see like:
bar.new (1, 2, 3, 4) {} foo.cinit 1 2 (3, 4) {} bar.init 1 2 3 4 foo.init (1, 2, 3, 4) {}
so, right fix here depends on you're trying do, it's 1 of these:
- add
__init__
method cython base class. - remove
super().__init__
call entirely. - change
super().__init__
not pass params. - add appropriate
__new__
method python subclass.
i suspect in case it's #2 want.
* it's worth noting __cinit__
isn't identical __new__
. instead of getting cls
parameter, partially-constructed self
object (where can trust __class__
, c attributes not python attributes or methods), __new__
methods of classes in mro have been called before __cinit__
; __cinit__
of bases gets called automatically instead of manually; don't return different object besides 1 that's been requested; etc. it's it's called before __init__
, , expected take pass-through parameters, in same way __new__
is.
Comments
Post a Comment