Ответ 1
Причиной вашей проблемы является то, что это метод 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__
, так и object.__new__
(и отбрасывает результат последнего), но я считаю, что уродливое обходное решение, а не правильный путь'. (EDIT: И кроме того, это не сработает. Я получаю TypeError: object.__new__(ActorBase) is not safe, use greenlet.greenlet.__new__()
от вызова object.__new__
.) Я открыл issue в диспетчере маршрутизации.
EDIT: эта проблема показалась несколько знакомой, и я просто немного поработал в источнике 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
сделать что-то подобное.