gpt4 book ai didi

Python abc 模块 : Extending both an abstract base class and an exception-derived class leads to surprising behavior

转载 作者:太空狗 更新时间:2023-10-29 21:49:58 24 4
gpt4 key购买 nike

扩展抽象基类和派生自“对象”的类的工作方式与您预期的一样:如果您尚未实现所有抽象方法和属性,则会出现错误。

奇怪的是,用扩展“异常”的类替换对象派生类允许您创建不实现所有必需的抽象方法和属性的类的实例。

例如:

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, in <module>
b = myConcreteClass_2()
TypeError: Can't instantiate abstract class myConcreteClass_2 with abstract methods foo

我知道“Exception”和“myCustomException”没有属性“foo”,那么为什么我要实例化“myCustomException”呢?

编辑:郑重声明,这是我最终采用的骇人听闻的解决方法。不是真正等效的,但适用于我的目的。

# "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."

最佳答案

看起来这是因为 __new__ BaseException 的方法不关心抽象方法/属性。

当您尝试实例化 myConcreteClass_1 时, 它最终调用了 __new__来自 Exception类(class)。什么时候想实例化myConcreteClass_2 , 它调用 __new__来自 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

Exception类不提供 __new__方法,但它是父级, BaseException , does :

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__ implementation for 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__没有。

关于Python abc 模块 : Extending both an abstract base class and an exception-derived class leads to surprising behavior,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24792881/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com