Причина вашей проблемы в том, что это метод object.__new__
который выполняет проверку создания экземпляра абстрактного класса, и в этом случае object.__new__
не вызывается: gevent.Greenlet
наследуется от greenlet.greenlet
, а greenlet.greenlet
является типом расширения C, реализация которого __new__
не вызывает object.__new__
в любой момент (см. href="https://github.com/python-greenlet/greenlet/blob/master/greenlet.c#L840" rel="noreferrer">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__
и object.__new__
(и отбрасывает результат последнего), но я бы посчитал это уродливым обходным путем, а не 'правильно'. (EDIT: И, кроме того, это не работает. Я получаю TypeError: object.__new__(ActorBase) is not safe, use greenlet.greenlet.__new__()
из вызова object.__new__
.) Я открыл проблема в системе отслеживания гринлетов.
РЕДАКТИРОВАТЬ: эта проблема показалась несколько знакомой, и я только что немного покопался в источнике Enthought Traits, который определяет CHasTraits
, реализованный на C, который действительно прекрасно работает с ABC. И его метод __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
сделать что-то подобное.
person
Mark Dickinson
schedule
07.12.2013
gevent.Greenlet
не может быть классом нового стиля, который может помешать работе механизма ABC. - person chepner   schedule 07.12.2013greenlet.greenlet
, что мешает. - person Mark Dickinson   schedule 07.12.2013