Gra*_* F. 7 python abstract-class exception multiple-inheritance abc
扩展抽象基类和从"对象"派生的类可以正常工作:如果您尚未实现所有抽象方法和属性,则会出现错误.
奇怪的是,用扩展"Exception"的类替换对象派生类允许您创建不实现所有必需的抽象方法和属性的类的实例.
例如:
import abc
# The superclasses
class myABC( object ):
__metaclass__ = abc.ABCMeta
@abc.abstractproperty
def foo(self):
pass
class myCustomException( Exception ):
pass
class myObjectDerivedClass( object ):
pass
# Mix them in different ways
class myConcreteClass_1(myCustomException, myABC):
pass
class myConcreteClass_2(myObjectDerivedClass, myABC):
pass
# Get surprising results
if __name__=='__main__':
a = myConcreteClass_1()
print "First instantiation done. We shouldn't get this far, but we do."
b = myConcreteClass_2()
print "Second instantiation done. We never reach here, which is good."
Run Code Online (Sandbox Code Playgroud)
... ...产量
First instantiation done. We shouldn't get this far, but we do.
Traceback (most recent call last):
File "C:/Users/grahamf/PycharmProjects/mss/Modules/mssdevice/sutter/sutter/test.py", line 28, in <module>
b = myConcreteClass_2()
TypeError: Can't instantiate abstract class myConcreteClass_2 with abstract methods foo
Run Code Online (Sandbox Code Playgroud)
我知道"Exception"因此"myCustomException"没有属性"foo",所以为什么我要实例化"myCustomException"?
编辑:为了记录,这是我最终进入的hackish变通方法.不是真正等同,但适用于我的目的.
# "abstract" base class
class MyBaseClass( Exception ):
def __init__(self):
if not hasattr(self, 'foo'):
raise NotImplementedError("Please implement abstract property foo")
class MyConcreteClass( MyBaseClass ):
pass
if __name__=='__main__':
a = MyConcreteClass()
print "We never reach here, which is good."
Run Code Online (Sandbox Code Playgroud)
看起来这是因为__new__
for 的方法BaseException
不在乎抽象方法/属性。
当您尝试实例化时myConcreteClass_1
,它最终__new__
从Exception
类中调用。当要实例化时myConcreteClass_2
,它调用__new__
from object
:
>>> what.myConcreteClass_1.__new__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: exceptions.Exception.__new__(): not enough arguments
>>> what.myConcreteClass_2.__new__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object.__new__(): not enough arguments
Run Code Online (Sandbox Code Playgroud)
本Exception
类没有提供一个__new__
方法,但它的母公司BaseException
,做:
static PyObject *
BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyBaseExceptionObject *self;
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
if (!self)
return NULL;
/* the dict is created on the fly in PyObject_GenericSetAttr */
self->dict = NULL;
self->traceback = self->cause = self->context = NULL;
self->suppress_context = 0;
if (args) {
self->args = args;
Py_INCREF(args);
return (PyObject *)self;
}
self->args = PyTuple_New(0);
if (!self->args) {
Py_DECREF(self);
return NULL;
}
return (PyObject *)self;
}
Run Code Online (Sandbox Code Playgroud)
将此与以下__new__
实现object
比较:
static PyObject *
object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
if (excess_args(args, kwds) &&
(type->tp_init == object_init || type->tp_new != object_new)) {
PyErr_SetString(PyExc_TypeError, "object() takes no parameters");
return NULL;
}
if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) {
PyObject *abstract_methods = NULL;
PyObject *builtins;
PyObject *sorted;
PyObject *sorted_methods = NULL;
PyObject *joined = NULL;
PyObject *comma;
_Py_static_string(comma_id, ", ");
_Py_IDENTIFIER(sorted);
/* Compute ", ".join(sorted(type.__abstractmethods__))
into joined. */
abstract_methods = type_abstractmethods(type, NULL);
if (abstract_methods == NULL)
goto error;
builtins = PyEval_GetBuiltins();
if (builtins == NULL)
goto error;
sorted = _PyDict_GetItemId(builtins, &PyId_sorted);
if (sorted == NULL)
goto error;
sorted_methods = PyObject_CallFunctionObjArgs(sorted,
abstract_methods,
NULL);
if (sorted_methods == NULL)
goto error;
comma = _PyUnicode_FromId(&comma_id);
if (comma == NULL)
goto error;
joined = PyUnicode_Join(comma, sorted_methods);
if (joined == NULL)
goto error;
PyErr_Format(PyExc_TypeError,
"Can't instantiate abstract class %s "
"with abstract methods %U",
type->tp_name,
joined);
error:
Py_XDECREF(joined);
Py_XDECREF(sorted_methods);
Py_XDECREF(abstract_methods);
return NULL;
}
return type->tp_alloc(type, 0);
}
Run Code Online (Sandbox Code Playgroud)
如您所见object.__new__
,当存在未被覆盖但未被覆盖的抽象方法时,有代码会引发错误BaseException.__new__
。