Какой метод использует Python 2 для печати кортежей?

Оператор Python print обычно печатает repr() своего ввода. Кортежи не являются исключением:

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

Но затем я наткнулся на какое-то странное поведение, в то время как возился с внутренними CPython. Короче говоря: если вы обманываете Python 2 в создании саморегуляционного кортежа, его печать напрямую ведет себя совершенно иначе, чем печать его представлений repr()/str()/unicode().

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

Так что же делает print? В попытке ответить на этот вопрос сам я упомянул ссылку на язык:

6.6. Оператор print

print оценивает каждое выражение по очереди и записывает полученный объект в стандартный вывод (см. ниже). Если объект не является строкой, он сначала преобразуется в строку, используя правила для преобразования строк.

И правила для преобразования строк:

5.2.9. Преобразования строк

Преобразование строк - это список выражений, заключенный в обратном порядке (a.k.a. назад):

string_conversion ::=  "`" expression_list "`"

Но включение outer в обратные кавычки имеет тот же результат, что и вызов repr() и друзей. Нет кубиков. Так что, черт возьми, print действительно делает за кулисами?

(Интересно, что поведение "фиксировано" в Python 3: печать саморегуляторного кортежа дает усеченную форму с эллипсией.)

Ответы

Ответ 1

Вы можете узнать, что на самом деле происходит, разобрав байт-код python.

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

И чтение источника для базовых кодов операций.

PRINT_ITEM в конечном итоге достигает этого блока кода:

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

Это означает, что __str__ или __repr__ будут вызываться, только если тип объекта не имеет функции tp_print. И tupleobject имеет один.

Если вы хотите понять внутренности CPython, лучший способ - прочитать исходный код. Я рекомендую серию руководств по внутренним элементам python, он объясняет все, что вы должны знать, чтобы полностью понять вывод функции python dis.