Because using str(obj)
must first go through type.__call__
then str.__new__
(create a new string) then PyObject_Str
(make a string out of the object) which invokes int.__str__
and, finally, uses the function you linked.
repr(obj)
, which corresponds to builtin_repr
, directly calls PyObject_Repr
(get the object repr) which then calls int.__repr__
which uses the same function as int.__str__
.
Additionally, the path they take through call_function
(the function that handles the CALL_FUNCTION
opcode that’s generated for calls) is slightly different.
From the master branch on GitHub (CPython 3.7):
str
goes through_PyObject_FastCallKeywords
(which is the one that callstype.__call__
). Apart from performing more checks, this also needs to create a tuple to hold the positional arguments (see_PyStack_AsTuple
).repr
goes through_PyCFunction_FastCallKeywords
which calls_PyMethodDef_RawFastCallKeywords
.repr
is also lucky because, since it only accepts a single argument (the switch leads it to theMETH_0
case in_PyMethodDef_RawFastCallKeywords
) there’s no need to create a tuple, just indexing of the args.
As your update states, this isn’t about int.__repr__
vs int.__str__
, they are the same function after all; it’s all about how repr
and str
reach them. str
just needs to work a bit harder.