문제

Python's print statement normally seems to print the repr() of its input. Tuples don't appear to be an exception:

>>> print (1, 2, 3)
(1, 2, 3)
>>> print repr((1, 2, 3))
(1, 2, 3)

But then I stumbled across some strange behavior while messing around with CPython's internals. In short: if you trick Python 2 into creating a self-referencing tuple, printing it directly behaves completely differently from printing its repr() / str() / unicode() representations.

>>> print outer   # refer to the link above
((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
... many lines later ...
((((((((((Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError: stack overflow
>>> print repr(outer)
((...),)
>>> print str(outer)
((...),)
>>> print unicode(outer)
((...),)

So what exactly is print doing? In an attempt to answer this question myself, I referred to the language reference:

6.6. The print statement

print evaluates each expression in turn and writes the resulting object to standard output (see below). If an object is not a string, it is first converted to a string using the rules for string conversions.

And the rules for string conversions are:

5.2.9. String conversions

A string conversion is an expression list enclosed in reverse (a.k.a. backward) quotes:

string_conversion ::=  "`" expression_list "`"

But enclosing outer in back quotes has the same result as calling repr() and friends. No dice. So what the heck is print actually doing behind the scenes?

(Interestingly, the behavior is "fixed" in Python 3: printing a self-referencing tuple gives the ellipsis-truncated form.)

도움이 되었습니까?

해결책

You can find out what is actually happening by disassembling python bytecode.

>>> from dis import dis
>>> dis(compile('print outer', '<string>', 'exec'))
  1           0 LOAD_NAME                0 (outer)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE

And reading the source for the underlying opcodes.

PRINT_ITEM eventually reaches this block of code:

else if (Py_TYPE(op)->tp_print == NULL) {
    PyObject *s;
    if (flags & Py_PRINT_RAW)
        s = PyObject_Str(op);
    else
        s = PyObject_Repr(op);
    ...
}
else
    ret = (*Py_TYPE(op)->tp_print)(op, fp, flags);

This means that __str__ or __repr__ will be called only if object's type does not have a tp_print function. And tupleobject has one.

If you want to understand the internals of CPython the best way is to read the source code. I recommend a series of tutorials on python internals, it explains everything you must know to fully understand the output of python dis function.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top