2013-08-15 46 views
6

Tôi đang cố gắng phân lớp pysam's Tabixfile lớp và thêm các thuộc tính bổ sung trên instantiation.Không thể ghi đè __init__ của lớp từ phần mở rộng của Cython

class MyTabixfile(pysam.Tabixfile): 

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

Khi tôi cố gắng để nhanh chóng MyTabixfile lớp con của tôi, tôi nhận được một 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 

Tôi cũng đã cố gắng kêu gọi các nhà xây dựng Tabixfile một cách rõ ràng:

class MyTabixfile(pysam.Tabixfile): 

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

nhưng điều này vẫn còn gây TypeError: object.__init__() takes no parameters.

Lớp này thực sự được triển khai trong Cython; mã constructor là dưới đây:

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) 

Tôi đọc qua Cython documentation on __cinit__ and __init__ mà nói

Bất kỳ đối số truyền cho constructor sẽ được chuyển đến cả __cinit__() phương pháp và phương pháp __init__(). Nếu bạn dự đoán subclassing kiểu mở rộng của bạn bằng Python, bạn có thể tìm thấy nó hữu ích để cung cấp cho các __cinit__() phương pháp *** luận để nó có thể chấp nhận và bỏ qua đối số thêm. Nếu không, bất kỳ lớp con Python nào có số có số __init__() có chữ ký khác sẽ phải ghi ghi đè __new__()1 cũng như __init__(), mà tác giả của một lớp Python sẽ không phải làm.

Các nhà phát triển pysam đã làm mất sự chăm sóc để thêm *args**kwargs với phương pháp Tabixfile.__cinit__, lớp con tôi __init__ phù hợp với chữ ký của __cinit__ vì vậy tôi không hiểu tại sao tôi không thể ghi đè lên khởi của Tabixfile .

Tôi đang phát triển với Python 3.3.1, Cython v.0.19.1 và pysam v.0.7.5.

Trả lời

17

Tài liệu hơi khó hiểu ở đây, ở chỗ nó giả định rằng bạn đã quen thuộc với việc sử dụng __new____init__.

Phương pháp __cinit__ là tương đương với một phương pháp __new__ bằng Python *

Giống như __new__, __cinit__không gọi bằng super().__init__ của bạn. nó được gọi trước khi Python thậm chí đến phương thức __init__ của lớp con của bạn. Lý do __cinit__ cần xử lý chữ ký của lớp con của bạn __init__ phương pháp là cùng một lý do chính xác __new__.

Nếu lớp con của bạn không rõ ràng gọi super().__init__, trông cho một phương pháp __init__ trong một lớp cha-một lần nữa, như __new__, một __cinit__ không phải là một __init__. Vì vậy, trừ khi bạn đã cũng được xác định là __init__, nó sẽ chuyển đến object.


Bạn có thể xem chuỗi bằng mã sau.

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) 

Khi chạy, bạn sẽ thấy một cái gì đó như:

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

Vì vậy, bên phải sửa chữa ở đây phụ thuộc vào những gì bạn đang cố gắng làm, nhưng đó là một trong những điều sau:

  1. Thêm phương thức __init__ vào lớp cơ sở Cython.
  2. Xóa hoàn toàn cuộc gọi super().__init__.
  3. Thay đổi super().__init__ để không chuyển bất kỳ thông số nào.
  4. Thêm phương thức __new__ thích hợp vào lớp con Python.

Tôi nghi ngờ trong trường hợp này là số 2 bạn muốn.


* Nó đáng chú ý là __cinit__ chắc chắn không phải là giống hệt nhau -__new__. Thay vì nhận được tham số cls, bạn nhận được đối tượng self được xây dựng một phần (nơi bạn có thể tin tưởng các thuộc tính __class__ và C nhưng không phải thuộc tính hoặc phương thức Python), các phương thức __new__ của tất cả các lớp trong MRO đã được gọi trước __cinit__; số __cinit__ của các căn cứ của bạn được gọi tự động thay vì theo cách thủ công; bạn không được trả lại một vật thể khác ngoài vật thể được yêu cầu; vv Nó chỉ là nó được gọi trước khi __init__, và dự kiến ​​sẽ có thông số pass-through, theo cùng một cách như __new__ là.

+1

code demo của bạn thực sự làm rõ dòng điều khiển. Cảm ơn bạn đã dành thời gian để thêm điều đó. Tôi đã đi với loại bỏ 'siêu() .__ init__' và nó làm việc đẹp và làm những gì tôi đã dự kiến ​​sẽ xảy ra trong nỗ lực của riêng tôi. – gotgenes

+0

Câu trả lời hoàn hảo, cắt và đủ rõ ràng! – pylover

1

Tôi đã nhận xét thay vì đăng câu trả lời nhưng tôi chưa có đủ stackOverflow foo.

Bài đăng của @ abarnert rất tuyệt vời và rất hữu ích. Tôi chỉ cần thêm một vài chi tiết cụ thể về pysam ở đây vì tôi vừa mới thực hiện subclassing trên pysam.AlignmentFile theo cách rất giống nhau.

Lựa chọn # 4 là sự lựa chọn sạch/đơn giản nhất có nghĩa là chỉ thay đổi trong __new__ lớp con của riêng tôi để lọc ra các params biết:

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) 

Nó cũng cần lưu ý rằng các lớp tập pysam dường như không có rõ ràng __init__ phương pháp, vì vậy bạn cũng cần phải bỏ qua param đi qua như mà đi thẳng đến đối tượng .__ init__ mà không chấp nhận các thông số:

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__() 
Các vấn đề liên quan