Python abc module: Extending both an abstract base class and an exception-derived class leads to surprising behavior
扩展一个抽象基类和一个从"object"派生的类可以如您所期望的那样工作:如果您还没有实现所有的抽象方法和属性,您会得到一个错误。
奇怪的是,将对象派生类替换为扩展"exception"的类允许您创建不实现所有必需抽象方法和属性的类的实例。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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." |
…产量…
1 2 3 4 5 | 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 |
我知道"exception"和"mycustomerexception"没有属性"foo",那么为什么我不去实例化"mycustomerexception"?
编辑:作为记录,这是我最终使用的黑客解决方案。不是真正的等价物,但为我的目的而工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #"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." |
看起来这是因为
当您试图实例化
1 2 3 4 5 6 7 8 | >>> 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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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; } |
与
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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); } |
如您所见,当抽象方法未被重写时,