Для чего предназначен оператор python "with"?
я наткнулся на Python with
заявление впервые сегодня. Я использую Python легко в течение нескольких месяцев и даже не знал о его существовании! Учитывая его несколько неясный статус, я подумал, что стоит спросить:
- что такое Python
with
заявление предназначен для использования? - что делать вы используете его для?
- есть ли
есть вещи, о которых мне нужно знать, или
общие анти-шаблоны, связанные с
его использование? Любой случай где лучше использовать
try..finally
чемwith
? - почему не используется более широко?
- какие стандартные классы библиотек совместимы с ним?
10 ответов
я считаю, что на это уже ответили другие пользователи до меня, поэтому я добавляю его только для полноты:
with
оператор упрощает обработку исключений путем инкапсуляции общих задач подготовки и очистки в так называемом контекст-менеджеров. Более подробную информацию можно найти в PEP 343. Например,open
оператор сам по себе является контекстным менеджером, который позволяет открывать файл, держать его открытым, пока выполняется контекстwith
оператор, где вы его использовали, и закройте его, как только вы покинете контекст, независимо от того, оставили ли вы его из-за исключения или во время регулярного потока управления. Thewith
таким образом, оператор может использоваться способами, аналогичными RAII pattern в C++: некоторый ресурс приобретаетсяwith
заявление и освобождается, когда вы покидаетеwith
контексте.-
некоторые примеры: открытие файлов с помощью
with open(filename) as fp:
, эквайринг замки с помощьюwith lock:
(гдеlock
примерthreading.Lock
). Вы также можете создать свои собственные менеджеры контекста, используяcontextmanager
декоратор отcontextlib
. Например, я часто использую это, когда мне нужно временно изменить текущий каталог, а затем вернуться туда, где я был:from contextlib import contextmanager import os @contextmanager def working_directory(path): current_dir = os.getcwd() os.chdir(path) try: yield finally: os.chdir(current_dir) 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 def redirected(**kwds): stream_names = ["stdin", "stdout", "stderr"] old_streams = {} try: for sname in stream_names: stream = kwds.get(sname, None) if stream is not None and 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 def temporary_dir(*args, **kwds): name = mkdtemp(*args, **kwds) try: yield name finally: shutil.rmtree(name) with temporary_dir() as dirname: # do whatever you want
Я бы предложил две интересные лекции:
1.
The with
оператор используется для обертывания выполнения блока методами, определенными контекстным менеджером. Это позволяет общие try...except...finally
шаблоны использования, которые должны быть инкапсулированы для удобного повторного использования.
2. Вы могли бы сделать что-то вроде:
with open("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)
with open('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
3.
Я не вижу здесь никакого антипаттерна.
Цитата погружение в Python:
попробовать..наконец-то хорошо. С лучше.
4.
Я думаю, это связано с привычкой программистов использовать try..catch..finally
заявление с других языков.
Питон with
оператор является встроенной языковой поддержкой Resource Acquisition Is Initialization
идиома, обычно используемая в C++. Он предназначен для безопасного приобретения и освобождения ресурсов операционной системы.
на with
оператор создает ресурсы в области/блок. Вы пишете свой код, используя ресурсы внутри блока. Когда блок выходит, ресурсы освобождаются независимо от результата кода в блоке (то есть, выходит ли блок обычно или из-за исключения).
многие ресурсы в библиотеке Python, которые подчиняются протоколу, требуемому with
заявление и поэтому может использоваться с ним из-из-коробки. Однако любой может сделать ресурсы, которые могут быть использованы в инструкции with, реализуя хорошо документированный протокол:PEP 0343
используйте его всякий раз, когда вы получаете ресурсы в приложении, которые должны быть явно освобождены, такие как файлы, сетевые подключения, блокировки и как.
примером антипаттерна может быть использование with
внутри цикла, когда было бы более эффективным, чтобы иметь with
вне цикла
for row in lines:
with open("outfile","a") as f:
f.write(row)
vs
with open("outfile","a") as f:
for row in lines:
f.write(row)
первый способ-Открытие и закрытие файла для каждого row
что может вызвать проблемы с производительностью по сравнению со вторым способом с открытием и закрытием файла только один раз.
снова для полноты я добавлю свой самый полезный вариант использования для 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
Я использую это много с помощью Гипергеометрического теста, который требует деления больших чисел в результате форм-факториалов. Когда вы делаете вычисления геномного масштаба, вы должны быть осторожны с ошибками округления и переполнения.
посмотреть PEP 343-оператор 'with', в конце есть примерный раздел.
... новая инструкция "with" для Python язык можно исключить стандартное использование операторов try/finally.
оператор with работает с так называемыми контекстными менеджерами:
http://docs.python.org/release/2.5.2/lib/typecontextmanager.html
идея в том, чтобы упростить обработку исключений, выполнив необходимую очистку после выхода с блока. Некоторые из встроенных модулей python уже работают в качестве контекстных менеджеров.
пункты 1, 2 и 3 достаточно хорошо покрыты:
4: он относительно новый, доступен только в python2.6+ (или python2.5 Использование from __future__ import with_statement
)
в python обычно"С" оператор используется для открытия файла, обработки данных, присутствующих в файле, а также для закрытия файла без вызова метода close (). оператор "with" упрощает обработку исключений, предоставляя действия по очистке.
общая форма с:
with open(“file name”, “mode”) as file-var:
processing statements
Примечание: нет необходимости закрывать файл, вызывая close () при file-var.close ()
еще один пример для поддержки из коробки, и тот, который может быть немного озадачивает сначала, когда вы привыкли к встроенному пути open()
ведет себя, являются connection
объекты популярных модулей базы данных, таких как:
на connection
объекты являются контекстными менеджерами и как таковые могут использоваться из коробки в with-statement
, однако, когда использовать выше внимание:
когда
with-block
закончено, либо с исключением, либо без,соединение не закрыто. В случаеwith-block
завершает с исключением, транзакция откатывается, в противном случае транзакция выполняется.
это означает, что программист должен позаботиться о том, чтобы закрыть соединение самостоятельно, но позволяет получить соединение и использовать его в нескольких with-statements
, как показано в psycopg2 docs:
conn = psycopg2.connect(DSN)
with conn:
with conn.cursor() as curs:
curs.execute(SQL1)
with conn:
with conn.cursor() as curs:
curs.execute(SQL2)
conn.close()
В приведенном выше примере, Вы заметите, что cursor
объекты psycopg2
также являются контекстными менеджерами. Из соответствующей документации по поведению:
когда
cursor
выходитwith-block
он закрыт, освобождая любой ресурс, в конечном итоге связанный с ним. Состояние транзакции не изменяется.