Всякий раз, когда я пишу несколько строк на python, у меня в голове всегда возникает вопрос, есть ли лучший способ написать этот код! Лучше не в смысле алгоритмического подхода, хотя это имеет значение, на самом деле это имеет значение больше всего, например, сокращение временных или пространственных сложностей или и того, и другого! Это то, что дает вашему подходу преимущество по сравнению с тем, как другие собираются решать ту же проблему. Но следующий важный момент, который выпадает сразу после этого, - пишете ли вы так, как должно быть?

Короче говоря, есть ли более питонический (и, конечно, лучший) способ написать тот же код?

Допустим, вы должны были записать некоторые данные json в файл.

Очевидный подход - открыть файл, записать данные json в файл и закрыть его. Этот простой интуитивно понятный подход был хорош только до тех пор, пока не было исключений.

Допустим, строка номер 16 вызывает ошибку, возможно, data не является допустимым json. В таком случае дальнейшее выполнение останавливается, и программа немедленно останавливается. Вы должны заметить здесь одну вещь: close() не был вызван на 18-й строке. В результате произошла утечка дескриптора файла, и существует большая вероятность того, что данные будут повреждены. Это ужасная проблема, особенно если вы работаете с файлами, которые важны для системы или бизнес-организации, на которую вы работаете.

Так что же делать, чтобы обойти эту проблему? Одно из возможных решений - использовать блок try-except, который не только обнаружит ошибку, но и в любом случае у нас есть возможность явно закрыть файл.

Однако размещение вашего кода в блоке try-except в большинстве случаев выглядит некрасиво, и лучшей и красивой альтернативой является использование context-manager.

Довольно простой синтаксис выглядит как

with context_manager as something:
     # do anything with something
     pass

Это эквивалентно блоку try-finally.

something = expression
something.__enter__()
try:
    # do anything with something
    pass
finally:
    something.__exit__()

Наша функция, переписанная с помощью оператора with, теперь выглядит более чистой.

Нам не нужно явно закрывать файл, обо всем этом позаботится оператор with. Конечно, что-то происходит под капотом. Чтобы разобраться в них более подробно, давайте создадим собственный контекстный менеджер.

Наш настраиваемый контекстный менеджер - это класс со (в частности) следующими двумя методами:

  • __enter__()
  • __exit__()

__enter__() вызывается во время вызова диспетчера контекста, а метод __exit__() вызывается для завершения кода, когда выполнение блока with завершено, то есть в конце блока. Таким образом, все, что, по вашему мнению, должно выполняться на этапе инициализации блока with, должно находиться внутри тела __enter__(). __exit__() должен содержать весь код очистки, который вы хотите выполнить в конце блока with. Код в теле __exit__() выполняется даже в тех случаях, когда мы обнаруживаем ошибки во время выполнения. Это гарантирует гарантированное выполнение действий по очистке, таких как закрытие файла в предыдущем случае, даже если во время вызовов json.loads() или json.dump() произошла ошибка.

Следует помнить, что переменные, определенные внутри блока with, имеют локальную область видимости, однако сам диспетчер контекста не имеет локальной области (с блоком with) и, следовательно, может использоваться повторно.

Менеджеры контекста могут использоваться для других целей, а не для простого ввода-вывода файлов, таких как открытие и закрытие сокетов, реализация функций настройки и удаления во время тестирования и тому подобное, о чем я не знаю.

Надеюсь, вам понравилось читать эту статью. Если у вас есть какие-либо предложения, пожалуйста, дайте мне знать в комментариях. Наконец, если вы нашли эту статью стоящей вашего времени, нажмите кнопку Рекомендовать, и я думаю, что кнопка следовать вызовет ревность, если вы оставите ее нетронутой. Спасибо за чтение, надеюсь, вы узнали что-то новое ;-)