Issue
Please see the below code snippet:
In [1]: class A(Exception):
...: def __init__(self, b):
...: self.message = b.message
...:
In [2]: class B:
...: message = "hello"
...:
In [3]: A(B())
Out[3]: __main__.A(<__main__.B at 0x14af96790>)
In [4]: class A:
...: def __init__(self, b):
...: self.message = b.message
...:
In [5]: A(B())
Out[5]: <__main__.A at 0x10445b0a0>
if A
subclasses from Exception
, its repr
returns a reference to B()
even though we only pass B()
's message attribute.
Why is this the intentional behavior in Exception.repr
and how does it work in python psuedocode if possible given the cpython code isn't too readable ?
Solution
When a Python object is created, it is the class's __new__
method that is called, and __init__
is then called on the new instance that the __new__
method returns (assuming it returned a new instance, which sometimes it doesn't).
Your overridden __init__
method doesn't keep a reference to b
, but you didn't override __new__
, so you inherit the __new__
method defined here (CPython source link):
static PyObject *
BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
// ...
if (args) {
self->args = args;
Py_INCREF(args);
return (PyObject *)self;
}
// ...
}
I omitted the parts that are not relevant. As you can see, the __new__
method of the BaseException
class stores a reference to the tuple of arguments used when creating the exception, and this tuple is therefore available for the __repr__
method to print a reference to the objects used to instantiate the exception. So it's this tuple which retains a reference to the original argument b
. This is consistent with the general expectation that repr
should return Python code which would create an object in the same state, if it can.
Note that it is only args
, not kwds
, which has this behaviour; the __new__
method doesn't store a reference to kwds
and __repr__
doesn't print it, so we should expect not to see the same behaviour if the constructor is called with a keyword argument instead of a positional argument. Indeed, that is what we observe:
>>> A(B())
A(<__main__.B object at 0x7fa8e7a23860>,)
>>> A(b=B())
A()
A bit strange since the two A
objects are supposed to have the same state, but that's how the code is written, anyway.
Answered By - kaya3
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.