扩展抽象基类和从"对象"派生的类可以正常工作:如果您尚未实现所有抽象方法和属性,则会出现错误.
奇怪的是,用扩展"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."
... ...产量
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, inb = myConcreteClass_2() TypeError: Can't instantiate abstract class myConcreteClass_2 with abstract methods foo
我知道"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."
dano.. 6
看起来这是因为__new__
for 的方法BaseException
不在乎抽象方法/属性。
当您尝试实例化时myConcreteClass_1
,它最终__new__
从Exception
类中调用。当要实例化时myConcreteClass_2
,它调用__new__
from object
:
>>> what.myConcreteClass_1.__new__() Traceback (most recent call last): File "", line 1, in TypeError: exceptions.Exception.__new__(): not enough arguments >>> what.myConcreteClass_2.__new__() Traceback (most recent call last): File " ", line 1, in TypeError: object.__new__(): not enough arguments
本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; }
将此与以下__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); }
如您所见object.__new__
,当存在未被覆盖但未被覆盖的抽象方法时,有代码会引发错误BaseException.__new__
。