Конструкция with#
В Python существует более удобный способ работы с файлами, чем те, которые использовались до сих пор — конструкция with :
In [1]: with open('r1.txt', 'r') as f: . for line in f: . print(line) . ! service timestamps debug datetime msec localtime show-timezone year service timestamps log datetime msec localtime show-timezone year service password-encryption service sequence-numbers ! no ip domain lookup ! ip ssh version 2 !
Кроме того, конструкция with гарантирует закрытие файла автоматически.
Обратите внимание на то, как считываются строки файла:
for line in f: print(line)
Когда с файлом нужно работать построчно, лучше использовать такой вариант.
В предыдущем выводе, между строками файла были лишние пустые строки, так как print добавляет ещё один перевод строки.
Чтобы избавиться от этого, можно использовать метод rstrip :
In [2]: with open('r1.txt', 'r') as f: . for line in f: . print(line.rstrip()) . ! service timestamps debug datetime msec localtime show-timezone year service timestamps log datetime msec localtime show-timezone year service password-encryption service sequence-numbers ! no ip domain lookup ! ip ssh version 2 ! In [3]: f.closed Out[3]: True
И конечно же, с конструкцией with можно использовать не только такой построчный вариант считывания, все методы, которые рассматривались до этого, также работают:
In [4]: with open('r1.txt', 'r') as f: . print(f.read()) . ! service timestamps debug datetime msec localtime show-timezone year service timestamps log datetime msec localtime show-timezone year service password-encryption service sequence-numbers ! no ip domain lookup ! ip ssh version 2 !
Открытие двух файлов#
Иногда нужно работать одновременно с двумя файлами. Например, надо записать некоторые строки из одного файла, в другой.
В таком случае, в блоке with можно открывать два файла таким образом:
In [5]: with open('r1.txt') as src, open('result.txt', 'w') as dest: . : for line in src: . : if line.startswith('service'): . : dest.write(line) . : In [6]: cat result.txt service timestamps debug datetime msec localtime show-timezone year service timestamps log datetime msec localtime show-timezone year service password-encryption service sequence-numbers
Это равнозначно таким двум блокам with:
In [7]: with open('r1.txt') as src: . : with open('result.txt', 'w') as dest: . : for line in src: . : if line.startswith('service'): . : dest.write(line) . :
Оператор With в Python
Python – язык программирования, наделенный достаточно большим количеством инструментов, функций и встроенных стандартных команд. С их помощью удается создавать совершенно разное программное обеспечение. Огромную роль в таких приложениях может сыграть менеджер контекста.
Базовые определения
Перед тем как начинать изучение любого инструмента языка программирования, нужно запомнить несколько базовых определений:
- Переменная – единица хранения информации. Является именованной областью памяти.
- Функция – блок кода, который выполняет определенные операции для получения задуманного результата.
- Оператор – объект, который умеет при помощи специальных команд манипулировать операндами.
- Операнд – объекты кода, которыми умеет управлять оператор.
- Алгоритм – набор инструкций и указаний, необходимых для решения поставленной изначально задачи.
В Питоне присутствует множество операторов. Один из них используется для формирования менеджера контекста. Далее речь зайдет о With в Python.
Предназначение With и менеджера
With появился в Питоне с версии 2.5. Данный оператор является достаточно полезным. Данная функция используется почти каждым написанным на языке приложением.
With выполняет различные действия при активации:
- обрабатывает открытие/закрытие ресурсов;
- закрывает автоматически часть приложения, с которой больше не нужно работать.
Все это необходимо для того, чтобы грамотно распределять ресурсы устройства, а также оптимизировать функционирование имеющейся памяти.
Менеджер в Питоне
The Python – простой и функциональный язык разработки приложений. В нем можно обрабатывать файлы. В C и некоторых других ЯП для этого требуется вручную открывать и закрывать документ. Ниже – фрагмент кода, который помогает это сделать:
With делает это автоматически. Каждый раз открывать и закрывать файлы самостоятельно при формировании приложения на The Python не придется. У оператора with имеется контекст (блок), в котором он будет действовать.
Когда приложение выходит из соответствующего контекста, with будет автоматически закрывать ранее используемый файл. Все это приводит к тому, что рассматриваемый элемент в the Python носит название «диспетчер контекста».
with open(‘input.txt’, ‘r’) as file_obj:
Выше – пример того, как данная «опция» используется при обработке файлов. The operator будет всегда в конце закрывать документ. Это относится даже к ситуациям, когда само приложение работает/завершается некорректно.
Применение
Возможность использования with in the statement реализована в большом количестве классов Питона. Данный элемент the code:
- Сохраняет ссылку на объект в объекте контекста. Так называют объект, содержащий дополнительную информацию о своем состоянии (область видимости, модуль и так далее).
- После создания объекта the operator вызывает метод под названием __enter__ dunder для соответствующего компонента. С его помощью происходит открытие ресурсов для объекта. Может использоваться при реализации сохранения состояния элемента.
При работе с with необходимо обратить внимание на ключевое слово as in the statement. Оно фактически возвращает объект контекста. As a используется для того, чтобы получить элемент, возвращаемый при помощи функции open().
As in statements можно не использовать, если у разработчика имеется ссылка на исходный объект контекста в другом месте.
Далее предстоит перейти во вложенный блок операторов. Когда он закончится или встретится исключение, приложение выполнит для объекта the context __exit__. Она выступает в качестве первой функции безопасности. Используется всегда для того, чтобы высвободить ресурсы устройства и выйти из заданного контекста.
Собственные менеджеры – как создать
Для более удобного и быстрого изучения in The Python оператора with as необходимо научиться создавать собственные context managers для заданного класса.
Выше – пример того, как происходит формирование упомянутого элемента в The Python. Здесь есть метод __init__. Он написан для обработчика, устанавливает начальное состояние объектов и соответствующих переменных.
Также есть метод __enter__, сохраняющий состояние объекта и открывающий его. Это позволяет попасть внутрь заданного блока. После выполнения соответствующего фрагмента кода диспетчер выполнит __exit__, чтобы восстановить прежнее состояние the object. Файл будет закрыт.
На экране появится такая надпись вследствие выполнения приложения.
Методы
При работе с Python with a statement, необходимо помнить о методах для менеджеров контекста. Их всего два:
- __enter__. Используется для того, чтобы войти в context времени выполнения. Либо возвращает текущий объект, либо другой связанный элемент. Возвращаемое значение будет привязано к идентификатору в виде предположения with.
- Метод __exit__. Применяется для возврата результата логического характера. Указывает на любое произошедшее исключение. Если есть одно исключение для with, оно будет переведено в конец блока.
Выше – наглядный пример того, как реализованы эти методы in The Python with statement. А вот результат, который будет выведен на экран.
Здесь можно увидеть наглядный пример использования with…as, but лучше всего закончить дистанционные компьютерные курсы по The Python, чтобы быстро освоить язык и всего его возможности.
with
Инструкция позволяет задействовать менеджер контекста для исполнения кода, находящего в её теле. Это, в частности, позволяет обособить блоки, использующие try except finally и повысить шансы их повторного использования.
class MyContextManager(object):
def __enter__(self):
print('enter')
def __exit__(self, exc_type, exc_value, traceback):
print('exit')
with MyContextManager(): # enter
print('do') # do
# exit
Последовательность действий инструкции
1. Выражение после with вычисляется для получения менеджера контекста;
2. Для последующего использования запоминается метод менеджера __exit__();
3. Вызывается метод менеджера __enter__();
4. Если после as указана цель, то в неё помещается результат __enter__() ;
На заметку
Инструкция гарантирует вызов __exit__() , в случаях, когда __enter__() отработал без ошибок. Если исключение происходит во время назначения цели as , то оно равноценно исключению, произошедшему в теле.
5. Выполняется тело инструкции;
6. Вызывается метод __exit__() . Если выход обусловлен исключением, то данные о нём передаются в метод. Если __exit__() вернул ложь , то по выходу из контекстного менеджера исключение продолжит своё восхождение по стеку. В случае истины , исключение будет подавлено.
+py3.1 Несколько менеджеров можно указать через запятую:
with manager1() as one, manager2() as two:
do()
# То же самое, вложенно:
with manager1() as one:
with manager2() as two:
do()
Блог питониста
Данная статья — перевод c моим небольшим дополнением, оригинал.
Часто ключевое слово with не до конца понятно даже опытным разработчикам.
Как и многие другие вещи в Python, ключевое слово with на самом деле очень просто устроено, это станет очевидно, как только вы поймете какую проблему оно решает. Посмотрите на данный код:
1 set things up 2 try: 3 do something 4 finally: 5 tear things down
Здесь под «set things up» подразумевается открытие файла, подключение какого-то внешнего ресурса, а под «tear things down» — закрытие файла, отключение от внешнего ресурса. Конструкция try-finally гарантирует, что «tear things down» часть будет всегда исполнена, даже если код, делающий что-либо вызовет ошибку или не завершится.
Если это часто ипользуется, то было бы удобно вынести код “set things up” и “tear things down” в библиотечную функцию, чтобы легко ее использовать. Конечно, вы можете сделать что-то вроде:
1 def controlled_execution(callback): 2 set things up 3 try: 4 callback(thing) 5 finally: 6 tear things down 7 8 def my_function(thing): 9 do something 10 11 controlled_execution(my_function)
Но это немного многословно, особенно если вам нужно изменять локальные переменные. Другой подход заключается в использовании генератора, а затем нужно использовать for-in:
1 def controlled_execution(): 2 set things up 3 try: 4 yield thing 5 finally: 6 tear things down 7 8 for thing in controlled_execution(): 9 do something with thing
Но yield нельзя было использовать внутри try-finally в 2.4 и раньше. И немного странно использовать loop для чего-то, что вы хотите выполнить один раз.
Поэтому, после рассмотрения нескольких вариантов, Гвидо Ван Россум и python-dev команда наконец решили использовать объект вместо генератора, чтобы контролировать поведение данного кода:
1 class controlled_execution: 2 def __enter__(self): 3 set things up 4 return thing 5 def __exit__(self, type, value, traceback): 6 tear things down 7 8 with controlled_execution() as thing: 9 some code
Теперь, когда «with» выражение исполняется, Python исполняет выражение, вызывает метод __enter__ с полученным значением (которое называется «context guard»), затем присваивает переменной переданной словом as (в данном случае thing) то, что возвращает метод __enter__. Далее, Python исполняет тело (в данное случае some code), и в любом случае вызывает метод __exit__.
В добавок, __exit__ может подавить исключение, вернуть вместо него True. Например, этот __exit__ заменяет TypeError, но разрешает все другие исключения:
1 def __exit__(self, type, value, traceback): 2 return isinstance(value, TypeError)
1 class controlled_execution: 2 def __enter__(self): 3 print('in enter') 4 print(self) 5 return ('test') 6 7 def __exit__(self, type, value, traceback): 8 print('in exit') 9 10 11 with controlled_execution() as thing: 12 print('in here') 13 print(thing) 14 raise TypeError
alex@vostro:~/projects/blog_materials$ python test.py in enter in here test in exit Traceback (most recent call last): File "test.py", line 14, in raise TypeError TypeError
1 class controlled_execution: 2 def __enter__(self): 3 print('in enter') 4 print(self) 5 return ('test') 6 7 def __exit__(self, type, value, traceback): 8 print('in exit') 9 return isinstance(value, TypeError) 10 11 12 with controlled_execution() as thing: 13 print('in here') 14 print(thing) 15 raise TypeError
alex@vostro:~/projects/blog_materials$ python test.py in enter in here test in exit
В Python 2.5 у объекта типа file появились методы __enter__ и __exit__, первый просто возвращает сам объект, а второй закрывает файл:
>>> f = open("x.txt") >>> f >>> f.__enter__() >>> f.read(1) 'X' >>> f.__exit__(None, None, None) >>> f.read(1) Traceback (most recent call last): File "", line 1, in ValueError: I/O operation on closed file
Поэтому, чтобы открыть файл, сделать что-то с содержимым и точно закрыть его, вы просто делаете так:
1 with open("x.txt") as f: 2 data = f.read() 3 do something with data
Это не было очень сложно, правда?