2013-08-15 1 views
6

pysam's Tabixfile 클래스의 하위 클래스를 만들고 인스턴스화시 추가 특성을 추가하려고합니다. Cython 확장에서 클래스의 __init__을 재정의 할 수 없습니다.

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 :

나는 또한 명시 적으로 Tabixfile 생성자를 호출하려고
>>> 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 

:

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를 발생시킵니다.

이 클래스는 실제로 Cython에서 구현됩니다. 생성자 코드는 다음과 같습니다 :

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) 

내가 생성자에 전달

모든 인수가 __cinit__() 방법과 __init__() 방법 모두에 전달됩니다 말한다 Cython documentation on __cinit__ and __init__ 읽어. 파이썬에서 확장 유형을 서브 클래스 예상되는 경우, 당신이 그것을 유용 에 찾을 수는 __cinit__() 방법 *** 인수가 동의 및 추가 인수를 무시할 수 있도록를 제공합니다. 그렇지 않으면, 다른 서명을 가진 __init__()이있는 파이썬 서브 클래스 는 의 작가가 파이썬 클래스가해야 할 기대하지 않을 것이다 재정 __new__()1뿐만 아니라 __init__()에있을 것이다.

pysam 개발자가 Tabixfile.__cinit__ 방법 *args**kwargs을 추가 알아서했다, 내 서브 클래스 __init__ 그래서 나는 내가 Tabixfile의 초기화를 오버라이드 (override) 할 수없는거야 이유를 이해하지 __cinit__의 서명과 일치 .

저는 파이썬 3.3.1, Cython v.0.19.1 및 pysam v.0.7.5로 개발 중입니다.

답변

17

설명서는 여기에 다소 혼란 스럽습니다. __new____init__을 잘 알고 있다고 가정합니다.

__cinit__ 방법은 파이썬에서 __new__ 방법과 거의 동일합니다 *

__new__처럼 __cinit__이다 아닌 super().__init__에 의해 호출.; 파이썬이 서브 클래스의 __init__ 메소드까지 가져 오기 전에 호출됩니다. 이유는 __cinit__은 서브 클래스 __init__ 메서드의 서명을 처리해야하므로 정확히 동일한 이유가 __new__입니다.서브 클래스가 명시 적으로 super().__init__를 호출 할 경우

은, 그건 __cinit____init__ 아니라, __new__처럼 슈퍼 클래스-다시의 __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 :

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

그래서 오른쪽 : 실행하면

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) 

, 당신은 같은 것을 볼 수 있습니다 여기서 해결할 작업은 수행하려는 작업에 따라 다르지만 다음 중 하나입니다.

  1. __init__ 메서드를 Cython 기본 클래스에 추가하십시오.
  2. super().__init__ 호출을 완전히 제거하십시오.
  3. super().__init__을 변경하여 매개 변수를 전달하지 마십시오.
  4. 적절한 __new__ 메서드를 Python 하위 클래스에 추가하십시오.

나는이 경우에 원하는 # 2라고 생각합니다.


은 * 그것은 __cinit__ 확실히 __new__- 동일 아니라고 지적 가치가있다. cls 매개 변수를 가져 오는 대신 __class__ 및 C 특성을 사용할 수 있지만 파이썬 특성이나 메서드는 신뢰하지 못하는 부분적으로 구성된 self 개체가 생기면 MRO의 모든 클래스에 대한 __new__ 메서드가 이미 __cinit__ 전에 호출되었습니다. 베이스의 __cinit__이 수동으로가 아니라 자동으로 호출됩니다. 당신은 요청 된 것과는 다른 객체를 반환하지 않습니다. __init__ 전에 호출되었으며 통과 매개 변수를 사용할 것으로 예상됩니다. __new__과 같은 방식입니다.

+1

귀하의 데모 코드는 정말 제어 흐름을 명확히 : 그 .__ init__ 매개 변수를 허용하지 않는 개체를 똑바로 간다 당신은 또한 PARAM 통과 생략 할 필요가 있으므로, 명시 적 __init__ 방법의가 있습니다. 추가 할 시간을 내 주셔서 감사합니다. 나는'super() .__ init__ '을 지우고 갔는데, 그것은 아름답게 작동했고, 내 자신의 시도에서 일어날 것으로 예상했던 것을합니다. – gotgenes

+0

완벽한 대답, 잘라 내기 & 분명히 충분! – pylover

1

답변을 게시하는 대신 댓글을 달았지만 아직 충분한 StackOverflow foo가 없습니다.

@ abarnert의 게시물은 우수하고 매우 유용합니다. 나는 pysam.AlignmentFile에서 서브 클래 싱을 한 것과 비슷한 방식으로 여기에 몇 가지 pysam 특성을 추가 할 것이다.또한 pysam 파일 클래스가 보이지 않는 것을 주목해야한다

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) 

:

옵션 # 4는 의미 깨끗한/쉬운 선택은 알 수없는 PARAMS을 걸러 내 자신의 서브 클래스 __new__의 변화였다

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__() 
관련 문제