Что такое python "с" утверждением?
-
26-09-2019 - |
Вопрос
Я наткнулся на Python with
Заявление впервые сегодня. Я легко использую Python в течение нескольких месяцев и даже не знал о его существовании! Учитывая его несколько неясный статус, я думал, что это будет стоить спрашивать:
- Что такое Python
with
Заявление, предназначенное для использования? - Для чего ты это используешь?
- Есть ли какие-либо готы, которые мне нужно знать, или общие антиблокировки, связанные с его использованием? Любые случаи, где лучше использовать
try..finally
чемwith
? - Почему он не используется более широко?
- Какие стандартные классы библиотеки совместимы с ним?
Решение
Я считаю, что это уже ответило другим пользователям перед собой, поэтому я только добавляю его ради полноты:
with
Заявление упрощает обработку исключения путем инкапсулирования общих задач препарата и очистки в так называемых контекстные менеджеры. Отказ Дополнительные детали можно найти в PEP 343.. Отказ Например,open
Заявление - это менеджер контекста сам по себе, который позволяет открыть файл, держать его открытым, пока выполнение находится в контекстеwith
Заявление, где вы использовали его, и закройте его, как только вы оставите контекст, независимо от того, оставили ли вы его из-за исключения или во время регулярного контрольного потока. Тоwith
Таким образом, заявление может использоваться способом, похожим на 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
Другие советы
Я бы предложил две интересные лекции:
- PEP 343. Оператор "с"
- Пошаговать Понимание утверждения Python "с"
1.То 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.Я не вижу здесь антипаттерн.
Цитирование Нырять в Питон:
Попробуйте .. Финально это хорошо. с лучше.
4.Я думаю, это связано с привычкой программистов для использования try..catch..finally
заявление с других языков.
Python with
Заявление встроено языковая поддержка Resource Acquisition Is Initialization
Идиома обычно используется в C ++. Предназначен, чтобы обеспечить безопасное приобретение и выпуск ресурсов операционной системы.
То with
Заявление создает ресурсы в пределах объема / блока. Вы пишете свой код, используя ресурсы в блоке. Когда блок выходит, ресурсы чисто выделяются независимо от исхода кода в блоке (то есть ли блок выходит нормально или из-за исключения).
Многие ресурсы в библиотеке Python, которые подчиняются протоколу, требуемым with
Заявление и так можно использовать с ним из коробки. Однако любой может сделать ресурсы, которые могут быть использованы в соответствии с заявлением, внедряя хорошо документированный протокол: PEP 0343.
Используйте его всякий раз, когда вы получаете ресурсы в своем приложении, которые должны быть явно отказаться, такие как файлы, сетевые соединения, блокировки и тому подобное.
Пример Антипаттерна может быть использовать with
внутри петли, когда было бы более эффективно иметь with
за пределами петли
Например
for row in lines:
with open("outfile","a") as f:
f.write(row)
против
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 - «С», В конце имеется пример секции.
... Новое заявление «С» на языке Python, чтобы сделать возможным факторов стандартных применений применения заявлений TRY / Наконец.
Очки 1, 2 и 3 быть достаточно хорошо освещены:
4: Это относительно новое, доступно только в Python2.6 + (или Python2.5, используя from __future__ import with_statement
)
С оператором работает с так называемыми контекстными менеджерами:
http://docs.cython.org/release/2.5.2/lib/typecontextmanager.html.
Идея состоит в том, чтобы упростить обработку исключения, выполняя необходимую очистку после выхода из блока «с». Некоторые из настроек Python уже работают как контекстные менеджеры.
Еще один пример для поддержки вне поля, и тот, который может быть в первую очередь с толку, когда вы используете к тому, как встроенный open()
ведет себя, есть connection
Объекты популярных модулей баз данных, такие как:
То connection
Объекты являются контекстными менеджерами и как таковые могут быть использованы вне коробки в with-statement
, Однако при использовании вышеуказанного примечания:
Когда
with-block
закончен, либо с исключением или без, Соединение не закрыто. Отказ В случаеwith-block
Заканчивается исключением, транзакция возвращается назад, в противном случае транзакция обязательна.
Это означает, что программист должен заботиться о закрытии самих соединений, но позволяет приобретать соединение и использовать его в нескольких with-statements
, как показано в Документы PSYCOPG2:
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
Он закрыт, освобождая любой ресурс в конечном итоге, связанным с ним. Состояние транзакции не затронута.
В питоне вообще "с участием«Заявление используется для открытия файла, обрабатывать данные, присутствующие в файле, а также закрыть файл без вызова метода CLACK (). «С» оператора делает исключение, обрабатывающую более простым, предоставляя мероприятия по очистке.
Общая форма с:
with open(“file name”, “mode”) as file-var:
processing statements
Примечание: Нет необходимости закрывать файл, вызывая закрытие () по файлу-var.close ()