What method does Python 2 use to print tuples?

cpython, python, python-2.7, python-internals, self-reference

Solution

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.

Problem

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.)

Original source