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