2013-08-15 12 views
6

Próbuję podklasy pysam's Tabixfile klasy i dodać dodatkowe atrybuty na wystąpienie.Nie można zastąpić klasy __init__ z rozszerzenia Cython

class MyTabixfile(pysam.Tabixfile): 

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

Kiedy próbuję instancję mój MyTabixfile podklasę, otrzymuję 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 

Próbowałem też wywołanie konstruktora Tabixfile wyraźnie:

class MyTabixfile(pysam.Tabixfile): 

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

ale to wciąż budzi TypeError: object.__init__() takes no parameters.

Ta klasa jest faktycznie zaimplementowana w Cython; kod konstruktor jest poniżej:

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) 

czytam przez Cython documentation on __cinit__ and __init__ który mówi

Wszelkie argumenty przekazywane do konstruktora zostaną przekazane zarówno metody __cinit__() i sposobu __init__(). Jeśli przewidujesz instacji typ rozszerzenia w Pythonie, może okazać się przydatna dać __cinit__() metoda * i ** argumenty tak, że można go akceptują i zignorować dodatkowe argumenty. W przeciwnym razie, każda Python podklasa który ma __init__() z innym podpisem będą musiały ręcznym __new__()1 jak __init__(), które pisarz klasa Python nie spodziewałby się zrobić.

Twórcy pysam nie podejmują starań, aby dodać *args i **kwargs metody Tabixfile.__cinit__, i mój podklasa __init__ pasuje podpis __cinit__ więc nie rozumiem, dlaczego jestem w stanie przesłonić inicjalizacji Tabixfile .

Pracuję z Python 3.3.1, Cython v.0.19.1 i pysam v.0.7.5.

Odpowiedz

17

Dokumentacja jest nieco myląca, ponieważ zakłada, że ​​znasz się na używaniu __new__ i __init__.

Sposób __cinit__ jest z grubsza odpowiednikiem __new__ metody w Pythonie *

Jak __new__, __cinit__ jest nie zwanego przez super().__init__.; jest wywoływana, zanim Python przejdzie do metody twojej podklasy: __init__. Powód, dla którego __cinit__ wymaga obsługi sygnatury Twojej podklasy __init__, jest taki sam, jak ten, który robi .

Jeśli podklasa ma jawnie wywołać super().__init__, że wygląda na metodzie in nadklasą-ponownie __init__, jak __new__, A __cinit__ nie jest __init__. Tak więc, chyba że masz zdefiniowane także jako zdefiniowane jako __init__, przejdzie ono do object.


Możesz zobaczyć sekwencję z następującym kodem.

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) 

Po uruchomieniu zobaczysz coś takiego:

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

tak, to w prawo Napraw to zależy od tego, co próbujesz zrobić, ale jest to jedna z tych rzeczy:

  1. Dodaj metodę __init__ do klasy bazowej Cython.
  2. Całkowicie usuń połączenie super().__init__.
  3. Zmień super().__init__, aby nie przekazywać żadnych parametrów.
  4. Dodaj odpowiednią metodę __new__ do podklasy Python.

Podejrzewam, że w tym przypadku jest to # 2, które chcesz.


* Warto zauważyć, że na pewno nie jest __cinit__identyczne do __new__. Zamiast uzyskiwania parametru cls uzyskuje się częściowo zbudowany obiekt self (w którym można ufać atrybutom __class__ i C, ale nie atrybutom ani metodom Pythona), metodywszystkich klas w MRO zostały już wywołane przed dowolnym __cinit__; __cinit__ twoich baz zostanie wywołany automatycznie zamiast ręcznie; nie otrzymasz zwrotu innego obiektu oprócz tego, który został zamówiony; itp. Po prostu jest on wywoływany przed __init__ i oczekuje się, że przejmie parametry tranzytu, w taki sam sposób jak __new__.

+1

Twój kod demo naprawdę wyjaśnia przepływ sterowania. Dziękujemy za poświęcenie czasu, aby to dodać. Poszedłem z usunięciem 'super() .__ init__' i zadziałało pięknie i robi to, co spodziewałem się w moich własnych próbach. – gotgenes

+0

Idealna odpowiedź, wystarczy wyciąć i wyczyścić! – pylover

1

Chciałbym komentować zamiast ogłaszać odpowiedź, ale nie mam jeszcze wystarczająco dużo StackOverflow foo.

@ posta abarnerta jest doskonałe i bardzo pomocne. Dodałbym tylko kilka szczegółów pysam tutaj, ponieważ właśnie zrobiłem podklasę na pysam.AlignmentFile w bardzo podobny sposób.

Option # 4 było najczystszym/najłatwiejszy wybór co oznaczało, zmienia się tylko w moim podklasy __new__ odfiltrować nieznanych 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) 

Należy również zauważyć, że klasy plików pysam nie wydają mieć wyraźny __init__ metoda'S, więc trzeba także pominąć param przejść jako że idzie prosto do object .__ init__ który nie akceptuje parametry:

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__() 
Powiązane problemy