🖨Зачем подменять стандартную функцию print()?
В Python3 директива print() стала функцией, то есть объектом, с которым мы можем делать что угодно. Во 2-м Python мы такого лишены.
Как можно изменить стандартное поведение этой функции?
Обычно подменяют объект sys.stdout, что работает и во 2-м. Но будем действовать через builtins. При этом изменим только функцию print(), а stdout останется прежним.
Что нам потребуется сделать?
1. Создать функцию, которая заменит print и не сломает стандартное поведение
2. Подменить объект builtins.print
import builtins
_print = builtins.print
builtins.print = lambda *a, **kw: _print(*a, **kw)
В примере выше я заменил print но не добавил никакого функционала. Сейчас print() работает как и прежде, только через посредника.
Давайте думать варианты.
🔸Изменить аргументы по умолчанию.
Порой во время разработки приложения требуется именно распечатывать информацию в консоль через print(). Если это сервер, то всякий раз приходится дописывать flush=True чтобы очищался буфер вывода и мы видели в консоли текст сразу же. Давайте сделаем так чтобы этот аргумент по умолчанию был True. Тогда лямбда будет выглядеть так:
lambda *a, **kw: _print(*a, **{**kw, 'flush': True})
🔸Поиск принтов в коде.
Приходилось пару раз рефакторить код сервера с очень запутанной структурой. На этапе избавления от принтов я не мог найти где распечатывается пустой список?! Ни одного принта по проекту нет а он распечатывается😢 Давайте заставим функцию print() сообщать нам где она находится.
import builtins, sys
_print = builtins.print
def _located_print(*args, **kwargs):
_print(*args, **kwargs)
f = sys._getframe().f_back
_print('=> File:', f.f_code.co_filename,
'| line', f.f_lineno)
builtins.print = _located_print
Выйдет примерно так:
>>> print('Hello')
Hello
=> File: /path/to/script.py | line: 1
🔸Как-либо модифицировать все принты.
Например добавлять что-то в начале, изменять цвет, заменять символьные смайлы на юникод и тд.
Вот пример от меня. В Python 3.8 добавили возможность через f-string распечатывать имя переменной вместе с её значением:
>>> value = 123
>>> print(f"{value=}")
value=123
Аналогичный функционал я повторил через подмену функции print().
🌎 Полный код здесь
🔸Отключить принт повсеместно!
Да, бывает и такое нужно). Кроме простого отключения (код сами догадайтесь какой) можно заменять всё на одинаковое сообщение, сделав принт бесполезным. Чтобы не использовали принт в проектах!
import builtins
_print = builtins.print
builtins.print = lambda *a, **kw: _print("Don't use prints!")
Получится что-то такое
>>> print('Debug message')
Don't use prints!
Но это больше похоже на шутку, реализовать которую помогут стартап скрипты, о которых юзер не догадывается 😝
🔸Заменить все принты на нормальное логирование.
Тоже вариант, но не особо полезный. Лучше писать нормальный логгинг чем так "костылять". Ну а для тренировки можно попробовать реализовать и этот вариант. Думаю, сами справитесь)
_________________________
Стоить ещё учесть пару моментов:
- эту подмену следует делать в самом начале работы приложения, в скрипте с которого начинается запуск. Удобно делать через стартап скрипт. Исходники при этом менять не требуется.
- функция изменится повсеместно во всех модулях на время сессии
- stdout не изменён, то есть обычный логгер будет писать в консоль нормально.
- Мы подменяем функцию на другую, а значит help(print) не покажет нам документацию, __name__ будет неверный и тд. Так что не забывайте использовать functools.wraps
- в моих примерах кое-где не обрабатывается kwargs. Не копируйте вслепую, всегда понимайте что делаете или не делайте вовсе.
Присылайте в коменты свои варианты! 😎
#tricks