Почему print стал функцией в python 3?

May 20, 2017 00:01 · 529 words · 3 minute read перевод python

Оригинал: ‘Why print became a function in Python 3’ by Brett Cannon

После моего поста “Почему Python3 получился именно таким”, в котором я ответил на самые популярный вопрос почему в python 3 разделили текстовые и бинарные данные, меня попросили ответить на второй по популярности вопрос: почему в python3 print стал функцией.

Кто это сделал?

В самом простом случае выражение print A является эквивалентом sys.stdout.write(str(A) + '\n'). Если вы укажите дополнительные аргументы через запятую, то они будут переданы в str() и выведены в одной строке через пробел. Например, print A, B, C равносильно sys.stdout.write(' '.join(map(str, [A, B, C])) + '\n'). Если выражение заканчивается запятой, после которой ничего нет print A, , то выполнится следующая конструкция sys.stdout.write(str(A)). В python 2.0 была добавлена конструкция перенаправления вывода: print >> output, A которая транслировалась в output.write(str(A) + '\n').

Определение функции print такое:

import sys

def print(*objects, sep=None, end=None, file=None, flush=False): 
    """A Python translation of the C code for builtins.print()."""
    if sep is None:
        sep = ' '
    if end is None:
        end = '\n'
    if file is None:
        file = sys.stdout
    file.write(sep.join(map(str, objects)) + end)
    if flush:
        file.flush()

Обратите внимание, что тут реализованы все возможности прежнего выражения print:

  • print A => print(A)
  • print A, B, C => print(A, B, C)
  • print A, => print(A, end='')
  • print >> output, A => print(A, file=output)

Возможность печати нескольких аргументов, перенаправление вывода, указание конца строки, - всё это по-прежнему доступно.

Это всё из-за гибкости

Однако, настоящая причина создания функции print заключается в гибкости: как для разработчиков, так и для создателей языка. В прошлом print можно было использовать только как инструкцию, сейчас же - как любую другую функцию - передавать в параметрах, распаковывать словарь в аргументах… Например, вам нужно печатать многоточие после каждой строки, чтобы показать, что работа ещё выполняется. В Python2 можно поступить двумя способами:

# Manually ...
print A, '...'

# For a reusable solution (which also works with a functional print) ...
def ellipsis_print(*args): 
    for arg in args:
        print arg, '',
    print '...'

Решение на Python3 выглядит более красивым:

# Manually ...
print(A, end='...\n')

# Multiple reusable solutions that won't work with a syntactic print...
ellipsis_print = lambda *args, **kwargs: print(*args, **kwargs, end='...\n') 
# Or ...
import functools 
ellipsis_print = functools.partial(print, end='...\n')

Другими словами, print как функцию можно передавать в качестве параметров, в то время как с синтаксической конструкцией такого сделать нельзя. Кроме того, её можно переопределить через builtins.print, что опять же невозможно для выражения.

Гибкость, которую получает команда разработчиков Python, освобождает их от необходимости развивать синтаксическую конструкцию, добавляя туда всё новые и новые фичи. Например, указание разделителя или окончания строки. Это довольно сложная проблема для дизайнеров языка, а для функции дело в добавлении всего пары аргументов. Богатство функциональных параметров даёт большую гибкость, нежели синтаксис языка.

Замечу, что в качестве общей рекомендации синтаксические конструкции зарезервированы для тех вещей, которые нельзя решить с помощью функций (либо можно, но с потерей читаемости). В случае print разница между print A и print(A) незначительна, и следовательно нет потери читаемости. К тому же нет и потери в функциональности - все варианты использования в Python2 так или иначе реализованы. Так что, убрав синтаксическую конструкцию print (а вместе с ней и ряд других), мы лишь упростили язык.