2013-12-06 10 views
10

Кажется, что когда у меня есть абстрактный базовый класс, который наследуется от gevent.Greenlet (который наследуется от окуляра модуля расширения C: https://github.com/python-greenlet/greenlet), то классы, которые его реализуют, не поднимают никаких ошибок abc о нереализованных методах.Могут ли абстрактные базовые классы python наследовать от C-расширений?

class ActorBase(gevent.Greenlet): 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractmethod 
    def foo(self): 
     print "foo" 

class ActorBaseTest(ActorBase): 
    def bar(self): 
     print "bar" 

abt = ActorBaseTest() # no errors! 

Если я наследовать от object он не как ожидалось:

class ActorBase(object): 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractmethod 
    def foo(self): 
     print "foo" 

class ActorBaseTest(ActorBase): 
    def bar(self): 
     print "bar" 

>>> abt = ActorBaseTest() 
Traceback (most recent call last): 
    File "/home/dw/.virtualenvs/prj/local/lib/python2.7/site-packages/IPython/core/interactiveshell.py", line 2827, in run_code 
exec code_obj in self.user_global_ns, self.user_ns 
    File "<ipython-input-6-d67a142e7297>", line 1, in <module> 
    abt = ActorBaseTest() 
TypeError: Can't instantiate abstract class ActorBaseTest with abstract methods foo 

Что такое правильный способ реализации этой функции?

+0

В целом, все должно быть в порядке. Однако 'gevent.Greenlet' не может быть классом нового стиля, который может помешать работе с машиной ABC. – chepner

+0

К сожалению, вы правы. gevent.Greenlet наследуется от модуля расширения C.Я отредактирую вопрос, чтобы спросить, какой хороший способ реализовать этот вариант использования. –

+1

Наследование от типа, реализованного на C, не должно быть проблемой, в общем. Должно быть что-то специфическое для самого класса greenlet.greenlet, который мешает. –

ответ

6

Причина вашей проблемы в том, что это object.__new__ метод, который делает проверку для конкретизации абстрактного класса, и в этом случае object.__new__ это не вызываются: gevent.Greenlet наследует от greenlet.greenlet и greenlet.greenlet является типом расширения C которого __new__ Реализация не вызывает object.__new__ в любой точке (см. Функцию green_new в источнике greenlet C).

Вы можете увидеть тот же эффект, подклассов некоторых других типов, встроенных реализующих свои собственные __new__ метод и не отсылают к object.__new__float типа, например). Однако проблема не относится к типам расширений C: вы также можете реплицировать ее с помощью чистых типов Python. Рассмотрим код ниже:

import abc 

class A(object): 
    def __new__(cls): 
     # self = object.__new__(cls) 
     return 42 

class B(A): 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractmethod 
    def foo(self): 
     pass 

b = B() # No exception. 

Класс B правильно зарегистрирован как абстрактный класс (внутренне, его Py_TPFLAGS_IS_ABSTRACT бит установлен в tp_flags поле), но object.__new__ никогда не называют, так что нет никакой ошибки, когда B инстанциируется , Однако, если вы раскомментируете вызов метода self = object.__new__(cls) в A, вы увидите ожидаемую ошибку при создании экземпляра.

Что касается «правильного пути» для реализации этого, к сожалению, я думаю, что правильный путь - исправить тип greenlet, чтобы его метод __new__ вызывал object.__new__. Я думаю, вы могли бы добавить метод __new__ к ActorBase, который явно вызывает как базовый класс __new__, так и (и отбрасывает результат последнего), но я считаю, что уродливое обходное решение, а не «правильный путь». (EDIT: И, кроме того, это не сработает. Я получаю TypeError: object.__new__(ActorBase) is not safe, use greenlet.greenlet.__new__() от звонка object.__new__.) Я открыл issue на отслеживании greenlet.


EDIT: Эта проблема казалась немного знакома, и я просто сделал некоторые копаться в Enthought Traits источник, который определяет CHasTraits класса, реализованный в C, который делает дружит с азами. И его метод __new__ начинается, как это (комментарии из первоисточника, а не мой):

PyObject * 
has_traits_new (PyTypeObject * type, PyObject * args, PyObject * kwds) { 

    // Call PyBaseObject_Type.tp_new to do the actual construction. 
    // This allows things like ABCMeta machinery to work correctly 
    // which is implemented at the C level. 
    has_traits_object * obj = (has_traits_object *) PyBaseObject_Type.tp_new(type, empty_tuple, empty_dict); 

Так что, возможно долгосрочное решение, чтобы убедить greenlet человек, чтобы сделать что-то подобное.

+0

Отличный ответ. Спасибо. –

 Смежные вопросы

  • Нет связанных вопросов^_^