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