2013-08-15 2 views
6

Я пытаюсь подкласса pysam's Tabixfile класса и добавлять дополнительные атрибуты при создании экземпляра.Не удается переопределить __init__ класса из расширения Cython

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.

Этот класс фактически реализован в Китоне; код конструктора ниже:

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__, так что я не понимаю, почему я не могу переопределить инициализацию Tabixfile ,

Я развиваюсь с помощью Python 3.3.1, Cython v.0.19.1 и pysam v.0.7.5.

ответ

17

Документация здесь немного запутанна, поскольку предполагается, что вы знакомы с использованием __new__ и __init__.

__cinit__ метод примерно эквивалентен методу __new__ в Python *

Как __new__, __cinit__ является не называется вашим super().__init__. он вызывается до того, как Python даже попадает в ваш метод __init__ вашего подкласса. Причина, по которой __cinit__ должна обрабатывать подпись вашего подкласса __init__, - это то же самое, что и __new__.

Если подкласс явно вызывать super().__init__, что ищет __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) 

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) 

При запуске, вы увидите что-то вроде:

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. Добавить метод __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__.

+1

Ваш демо-код действительно проясняет поток управления. Спасибо, что нашли время, чтобы добавить это. Я пошел с удалением 'super() .__ init__', и он работал красиво и делает то, что я ожидал в моих собственных попытках. – gotgenes

+0

Отличный ответ, cut & ясно достаточно! – pylover

1

Я бы прокомментировал, а не отправлял ответ, но пока у меня недостаточно StackOverflow foo.

@ сообщение abarnert отлично и очень полезно. Я бы просто добавил несколько особенностей pysam здесь, поскольку я только что сделал подклассу на pysam.AlignmentFile очень похожим образом.

Вариант № 4 был чистым/простой выбор, который имел в виду только изменения в моем собственном подклассе __new__, чтобы отфильтровать неизвестных Params:

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__ метод, так что вы также должны пропустить парам пройти через, как идет прямо к объекту .__ 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__() 
Смежные вопросы