Сегодня я впервые столкнулся с оператором Python with. Я несколько месяцев слегка пользовался Python и даже не знал о его существовании! Учитывая его несколько неясный статус, я подумал, что стоит спросить:
Для чего предназначен оператор Python with?
Для чего вы его используете?
Есть ли какие-либо ошибки, о которых мне нужно знать, или общие антишаблоны, связанные с его использованием? Есть ли случаи, когда его лучше использовать try..finally чем with?
Почему он не используется более широко?
Какие стандартные библиотечные классы совместимы с ним?
Переведено автоматически
Ответ 1
Я полагаю, что на этот вопрос уже отвечали другие пользователи до меня, поэтому я добавляю его только для полноты картины: with оператор упрощает обработку исключений, инкапсулируя общие задачи подготовки и очистки в так называемые контекстные менеджеры. Более подробную информацию можно найти в PEP 343 . Например, open инструкция сама по себе является контекстным менеджером, который позволяет вам открывать файл, сохранять его открытым до тех пор, пока выполнение выполняется в контексте with инструкции, в которой вы его использовали, и закрывать его, как только вы выходите из контекста, независимо от того, вышли ли вы из-за исключения или во время обычного потока управления. Таким образом, оператор with может использоваться способами, аналогичными шаблону RAII в C ++: некоторый ресурс приобретается with оператором и освобождается, когда вы покидаете with контекст.
Вот несколько примеров: открытие файлов с помощью with open(filename) as fp:, получение блокировок с помощью with lock: (где lock - экземпляр threading.Lock). Вы также можете создавать свои собственные контекстные менеджеры, используя contextmanager декоратор из contextlib. Например, я часто использую это, когда мне нужно временно изменить текущий каталог, а затем вернуться туда, где я был.:
with working_directory("data/stuff"): # do something within data/stuff # here I am back again in the original working directory
Вот еще один пример, который временно перенаправляет sys.stdin, sys.stdout и sys.stderr на какой-либо другой дескриптор файла и восстанавливает их позже:
from contextlib import contextmanager import sys
@contextmanager defredirected(**kwds): stream_names = ["stdin", "stdout", "stderr"] old_streams = {} try: for sname in stream_names: stream = kwds.get(sname, None) if stream isnotNoneand stream != getattr(sys, sname): old_streams[sname] = getattr(sys, sname) setattr(sys, sname, stream) yield finally: for sname, stream in old_streams.iteritems(): setattr(sys, sname, stream)
with redirected(stdout=open("/tmp/log.txt", "w")): # these print statements will go to /tmp/log.txt print"Test entry 1" print"Test entry 2" # back to the normal stdout print"Back to normal stdout again"
И, наконец, еще один пример, который создает временную папку и очищает ее при выходе из контекста:
from tempfile import mkdtemp from shutil import rmtree
@contextmanager deftemporary_dir(*args, **kwds): name = mkdtemp(*args, **kwds) try: yield name finally: shutil.rmtree(name)
with temporary_dir() as dirname: # do whatever you want
1. Оператор with используется для завершения выполнения блока методами, определенными контекстным менеджером. Это позволяет инкапсулировать общие try...except...finally шаблоны использования для удобного повторного использования.
2. Вы могли бы сделать что-то вроде:
withopen("foo.txt") as foo_file: data = foo_file.read()
или
from contextlib import nested with nested(A(), B(), C()) as (X, Y, Z): do_something()
ИЛИ (Python 3.1)
withopen('data') as input_file, open('result', 'w') as output_file: for line in input_file: output_file.write(parse(line))
или
lock = threading.Lock() with lock: # Critical section of code
4. Я предполагаю, что это связано с привычкой программистов использовать try..catch..finally операторы из других языков.
Ответ 3
Оператор Python with является встроенной языковой поддержкой Resource Acquisition Is Initialization идиомы, обычно используемой в C ++. Он предназначен для обеспечения безопасного получения и высвобождения ресурсов операционной системы.
Оператор with создает ресурсы в области видимости / блоке. Вы пишете свой код, используя ресурсы внутри блока. При завершении работы блока ресурсы полностью освобождаются независимо от результата выполнения кода в блоке (то есть завершается ли блок нормально или из-за исключения).
Многие ресурсы в библиотеке Python, которые подчиняются протоколу, требуемому оператором with, и поэтому могут использоваться с ним "из коробки". Однако любой может создать ресурсы, которые можно использовать в операторе with, реализовав хорошо документированный протокол: PEP 0343
Используйте его всякий раз, когда вы приобретаете ресурсы в своем приложении, от которых необходимо явно отказаться, такие как файлы, сетевые подключения, блокировки и тому подобное.
Ответ 4
Еще раз для полноты картины я добавлю свой самый полезный пример использования для with операторов.
Я много занимаюсь научными вычислениями, и для некоторых действий мне нужна Decimal библиотека для вычислений произвольной точности. Для какой-то части моего кода мне нужна высокая точность, а для большинства других частей мне нужна меньшая точность.
Я устанавливаю точность по умолчанию на низкое число, а затем использую with, чтобы получить более точный ответ для некоторых разделов:
from decimal import localcontext
with localcontext() as ctx: ctx.prec = 42# Perform a high precision calculation s = calculate_something() s = +s # Round the final result back to the default precision
Я часто использую это с гипергеометрическим тестом, который требует разделения больших чисел, образующих форм-факториалы. При вычислениях в геномном масштабе вы должны быть осторожны с ошибками округления и переполнения.