【问题标题】:Python exception - how does the args attribute get automatically set?Python 异常 - 如何自动设置 args 属性?
【发布时间】:2017-12-01 16:04:45
【问题描述】:

假设我定义了以下异常:

>>> class MyError(Exception):
...     def __init__(self, arg1):
...         pass

然后我实例化这个类来创建一个异常对象:

>>> e = MyError('abc')
>>> e.args
('abc',)

args 属性是如何设置的? (在__init__,我什么都不做。)

【问题讨论】:

标签: python python-3.x exception


【解决方案1】:

它是在BaseException.__new__() 方法中设置的,可以在这里看到:source code

注意:在 Python 2.7 中,它是在 BaseException.__init__() 方法中设置的,因此覆盖使 .args dict 始终为空(不确定是否指向正确的行):source code

【讨论】:

  • @Deb 当你输入e = MyError('abc')时,继承转换为Exception('abc')
  • @PRMoureu 不,如果被覆盖,则应显式调用超类构造函数。出于这个原因,它在 Python 2.7 中不起作用,因为 args 属性是在构造函数中设置的,而不是在 __new__() 方法中。
  • @PRMoureu 在这方面的行为是相同的。如果要执行覆盖的超类方法,则应显式调用它。但是__new__() 方法有一些我还没有完全理解的魔力...docs.python.org/3.6/reference/datamodel.html#object.__new__
【解决方案2】:

args 被实现为具有__get____set__ 方法的数据描述符。

这发生在BaseException.__new__ 内部,就像@bakatrouble 提到的那样。除此之外,BaseException.__new__ 内部发生的事情大致类似于下面的 Python 代码:

class BaseException:
    def __new__(cls, *args): 
        # self = create object of type cls
        self.args = args  # This calls: BaseException.args.__set__(self, args) 
        ...
        return self

Python 3.7.0 alpha 1的 C 代码中,上面的 Python 代码如下所示(检查 Python 的 C 代码是否存在过去或未来的差异): p>

BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    # other things omitted... 
    self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
    # many things follow... 
    if (args) {
        self->args = args;
        Py_INCREF(args);
        return (PyObject *)self;

    }
    # many more things follow
}

交互式实验:

>>> e = Exception('aaa')
>>> e
Exception('aaa',)

>>> BaseException.args.__set__(e, ('bbb',))
>>> e
Exception('bbb',)
>>> BaseException.args.__get__(e)
('bbb',)

因此,args 的神奇灵感让你的眼睛看起来像天堂一样发生在BaseException.__new__ 中,当BaseException 或其任何子类的对象被创建时。

【讨论】:

  • Exception 类也有 __new__ 方法。按照继承层次结构,不应该调用这个方法而不是BaseException.__new__吗?
  • @Deb 基本上所有异常类都使用与BaseException 相同的方法,包括__init____new__Exception 扩展了 BaseException,如果您特别查看宏内部的 C source code,您会注意到宏 SimpleExtendsException 同时使用了BaseException_newBaseException_init。由于我不是 C 精灵,欢迎其他成员的任何验证或更正。但对我来说,这显然是 99% 的确定性。
  • @Deb 请注意,如果您在 Python 中检查 BaseException.__dict__['__new__'] is Exception.__dict__['__new__'],这是 False,但它们可能是相同的!就像os.unlik and os.remove 的情况一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 1970-01-01
相关资源
最近更新 更多