2013-08-15 11 views
6

Ich versuche, pysam's Tabixfile Klasse Unterklasse und zusätzliche Attribute bei Instanziierung hinzufügen.Kann nicht überschreiben __init__ der Klasse von Cython-Erweiterung

class MyTabixfile(pysam.Tabixfile): 

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

Wenn ich versuche, meine MyTabixfile Unterklasse zu instanziiert, erhalte ich eine 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 

Ich versuchte auch explizit die Tabixfile Konstruktor aufrufen: TypeError: object.__init__() takes no parameters

class MyTabixfile(pysam.Tabixfile): 

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

aber noch erhöht.

Diese Klasse ist tatsächlich in Cython implementiert; der Konstruktor-Code ist unter:

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) 

ich durch die Cython documentation on __cinit__ and __init__ lesen, sagt

Alle Argumente an den Konstruktor übergeben wird sowohl für die __cinit__() Methode übergeben werden und die __init__() Methode. Wenn Sie Subklassen Ihre Erweiterungstyp in Python antizipieren, Sie geben die __cinit__() Verfahren nützlich finden * und ** Argumente, so dass es akzeptieren und zusätzliche Argumente ignorieren. Andernfalls jede Python-Unterklasse, die ein __init__() mit einer anderen Signatur muss Überschreibung __new__()1 sowie __init__(), die die Schreiber eine Python-Klasse nicht erwarten würde zu tun zu haben.

Die pysam Entwickler die Sorgfalt taten *args und **kwargs zur Tabixfile.__cinit__ Methode hinzuzufügen, und meine Unterklasse __init__ stimmt mit der Unterschrift von __cinit__ so verstehe ich nicht, warum ich bin nicht in der Lage die Initialisierung von Tabixfile außer Kraft zu setzen .

Ich entwickle mit Python 3.3.1, Cython v.0.19.1 und Pysam v.0.7.5.

Antwort

17

Die Dokumentation ist hier etwas verwirrend, da davon ausgegangen wird, dass Sie mit der Verwendung von __new__ und __init__ vertraut sind.

Die __cinit__ Methode entspricht in etwa eine __new__ Methode in Python *

Wie __new__, __cinit__ ist nicht von Ihrem super().__init__ genannt. Es wird aufgerufen, bevor Python die __init__ Methode Ihrer Unterklasse erreicht. Der Grund __cinit__ muss mit der Signatur Ihrer Unterklasse __init__ Methoden ist genau der gleiche Grund, __new__ funktioniert.

Wenn Ihre Unterklasse explizit super().__init__ nicht nennen, das sucht nach einer __init__ Methode in einer Oberklasse-wieder, wie __new__, ein __cinit__ ist keine __init__. Also, es sei denn, Sie haben auch definiert __init__, wird es durch object gehen.


Sie können die Sequenz mit dem folgenden Code sehen.

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:

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

Also, rechts:

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) 

Wenn er gestartet wird, werden Sie so etwas wie sehen Fix hier hängt davon ab, was Sie versuchen zu tun, aber es ist einer von diesen:

  1. Fügen Sie der Cython-Basisklasse eine __init__-Methode hinzu.
  2. Entfernen Sie den super().__init__ Aufruf vollständig.
  3. Ändern Sie die super().__init__, um keine Parameter zu übergeben.
  4. Fügen Sie der Python-Unterklasse eine geeignete __new__-Methode hinzu.

Ich vermute in diesem Fall ist es # 2 Sie wollen.


* Es ist erwähnenswert, dass __cinit__ definitiv nicht identisch-__new__ ist. Anstatt einen cls Parameter zu erhalten, erhalten Sie ein teilweise konstruiertes self Objekt (wo Sie __class__ und C-Attribute, aber nicht Python-Attribute oder Methoden vertrauen können), die __new__ Methoden aller Klassen in der MRO wurden bereits vor allen __cinit__ aufgerufen; Die __cinit__ Ihrer Basen wird automatisch anstelle von manuell aufgerufen; Sie können kein anderes Objekt als das angeforderte zurückgeben. usw. Es ist nur, dass es vor dem __init__ aufgerufen wird, und erwartet, Pass-Through-Parameter zu nehmen, in der gleichen Weise wie __new__ ist.

+1

Ihr Demo-Code verdeutlicht wirklich den Steuerungsablauf. Danke, dass Sie sich die Zeit genommen haben, das hinzuzufügen. Ich ging mit dem Entfernen von 'super() .__ init__' und es funktionierte wunderbar und macht, was ich bei meinen eigenen Versuchen erwartet hatte. – gotgenes

+0

Perfekte Antwort, schneiden und klar genug! – pylover

1

Ich hätte eher kommentiert, als eine Antwort zu posten, aber ich habe noch nicht genug StackOverflow foo.

@ abarnerts Beitrag ist ausgezeichnet und sehr hilfreich. Ich würde hier nur ein paar Pysam-Details hinzufügen, da ich gerade auf pysam.AlignmentFile auf sehr ähnliche Weise Subclassing gemacht habe.

Option # 4 war das sauberste/einfachste Wahl, die nur in meiner eigenen Unterklasse __new__ ändert dazu gedacht, die unbekannte params zum Ausfiltern:

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) 

Es ist auch, dass die pysam Dateiklassen scheinen nicht beachtet werden soll explizite __init__ Methode des haben, so dass Sie auch .__ init__ zum Objekt param passiert weglassen müssen, wie die gerade geht, welche Parameter nicht akzeptiert:

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__() 
Verwandte Themen