Для чего предназначен оператор Python with?

Сегодня я впервые наткнулся на оператор Python with. Я легко использую Python в течение нескольких месяцев и даже не знал о его существовании! Учитывая его несколько неясный статус, я подумал, что стоит спросить:

  1. Для чего предназначен оператор Python with?
  2. Для чего ты это используешь?
  3. Есть ли какие-то подводные камни, о которых мне нужно знать, или общие анти-паттерны, связанные с его использованием? Есть ли случаи, когда лучше использовать try..finally, чем with?
  4. Почему он не используется более широко?
  5. Какие стандартные классы библиотеки совместимы с ним?

person fmark    schedule 10.06.2010    source источник
comment
Для записи: здесь with в документации Python 3.   -  person Alexey    schedule 18.03.2017
comment
исходящий из фона Java, это помогает мне запомнить его как соответствующие ресурсы try with в Java, даже если это может быть не совсем правильно.   -  person vefthym    schedule 13.05.2019
comment
Для записи, вот PEP-0343: python.org/dev/peps/ pep-0343   -  person Priyanka    schedule 09.09.2020


Ответы (11)


  1. Я считаю, что на этот вопрос уже ответили другие пользователи до меня, поэтому я добавляю его только для полноты: оператор with упрощает обработку исключений, инкапсулируя общие задачи подготовки и очистки в так называемый менеджеры контекста. Более подробную информацию можно найти в PEP 343. Например, оператор open сам по себе является диспетчером контекста, который позволяет вам открывать файл, держать его открытым до тех пор, пока выполнение находится в контексте оператора with, в котором вы его использовали, и закрывать его, как только вы покидаете контекст, независимо от того, покинули ли вы его из-за исключения или во время обычного потока управления. Таким образом, оператор with может использоваться способами, аналогичными шаблону RAII в C ++: некоторый ресурс приобретается оператором with и освобождается, когда вы выходите из контекста with.

  2. Вот некоторые примеры: открытие файлов с помощью 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
    
person Tamás    schedule 10.06.2010
comment
Спасибо за добавление сравнения в RAII. Как программист на C ++, который рассказал мне все, что мне нужно было знать. - person Fred Thomsen; 01.09.2014
comment
Хорошо, позвольте мне прояснить это. Вы говорите, что оператор with предназначен для заполнения переменной данными до тех пор, пока инструкции под ним не будут выполнены, а затем освободить переменную? - person Musixauce3000; 20.05.2016
comment
Потому что я использовал его для открытия скрипта py. with open('myScript.py', 'r') as f: pass. Я ожидал, что смогу вызвать переменную f, чтобы увидеть текстовое содержимое документа, поскольку это то, что появилось бы, если бы документ был назначен f с помощью обычного оператора open: f = open('myScript.py').read(). Но вместо этого я получил следующее: <_io.TextIOWrapper name='myScript.py' mode='r' encoding='cp1252'>. Что это значит? - person Musixauce3000; 20.05.2016
comment
@ Musixauce3000 - использование with не устраняет необходимости read в самом файле. with вызывает open - он не знает, что вам нужно с ним делать - например, вы можете выполнить поиск. - person Tony Suffolk 66; 21.06.2016
comment
@ Musixauce3000 Оператор with может заполнять переменную данными или вносить другие изменения в среду до тех пор, пока инструкции под ним не будут выполнены, а затем выполняет любую необходимую очистку. Виды очистки, которые могут быть выполнены, - это такие вещи, как закрытие открытого файла или, как это делает @Tamas в этом примере, изменение каталогов обратно на то место, где вы были раньше, и т. Д. Поскольку в Python есть сборка мусора, освобождение переменной не имеет значения. вариант использования. with обычно используется для других видов очистки. - person Bob Steinke; 07.10.2019

Предлагаю две интересные лекции:

  • PEP 343 Заявление with
  • Effbot Общие сведения Оператор Python with

1. Оператор with используется для обертывания выполнения блока методами, определенными диспетчером контекста. Это позволяет инкапсулировать общие try...except...finally шаблоны использования для удобного повторного использования.

2. Вы можете сделать что-нибудь вроде:

with open("foo.txt") as foo_file:
    data = foo_file.read()

OR

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))

OR

lock = threading.Lock()
with lock:
    # Critical section of code

3. Я не вижу здесь антипаттерна.
Цитата Погрузитесь в Python:

попробуй .. наконец-то хорошо. с лучше.

4. Я думаю, это связано с привычкой программистов использовать try..catch..finally оператор из других языков.

person systempuntoout    schedule 10.06.2010
comment
Это действительно важно, когда вы имеете дело с объектами синхронизации потоков. Относительно редко в Python, но когда они вам нужны, вам действительно понадобится with. - person detly; 10.06.2010
comment
diveintopython.org не работает (навсегда?). Отображается на diveintopython.net - person snuggles; 11.02.2015
comment
Пример хорошего ответа, открытый файл является ярким примером, который показывает за кулисами открытия, io, закрытие файловых операций, которые чисто скрыты с настраиваемым ссылочным именем - person Angry 84; 13.10.2016
comment
Еще один небольшой пример использования ключевого слова with при работе с файловыми объектами приведен в документации: docs.python.org/3/tutorial/ - person Priyanka; 09.09.2020

Оператор Python with - это встроенная языковая поддержка идиомы Resource Acquisition Is Initialization, обычно используемой в C ++. Он предназначен для безопасного получения и освобождения ресурсов операционной системы.

Оператор with создает ресурсы в пределах области / блока. Вы пишете свой код, используя ресурсы внутри блока. Когда блок выходит из блока, ресурсы полностью высвобождаются независимо от результата кода в блоке (то есть выходит ли блок нормально или из-за исключения).

Многие ресурсы в библиотеке Python, которые подчиняются протоколу, требуемому оператором with, и поэтому могут использоваться с ним из коробки. Однако любой может создавать ресурсы, которые можно использовать в выражении with, путем реализации хорошо документированного протокола: PEP 0343

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

person Tendayi Mawushe    schedule 10.06.2010

Примером антипаттерна может быть использование 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, что может вызвать проблемы с производительностью по сравнению со вторым способом, когда файл открывается и закрывается только один раз.

person John La Rooy    schedule 10.06.2010

Снова для полноты я добавлю свой наиболее полезный вариант использования для операторов 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

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

person JudoWill    schedule 10.06.2010

См. PEP 343 - оператор 'with', там есть пример раздела на конец.

... новый оператор "with" в язык Python, чтобы можно было исключить стандартное использование операторов try / finally.

person stefanB    schedule 10.06.2010

пункты 1, 2 и 3 достаточно хорошо рассмотрены:

4: он относительно новый, доступен только в python2.6 + (или python2.5 с использованием from __future__ import with_statement)

person cobbal    schedule 10.06.2010

Оператор with работает с так называемыми менеджерами контекста:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

Идея состоит в том, чтобы упростить обработку исключений, выполнив необходимую очистку после выхода из блока with. Некоторые встроенные модули Python уже работают как менеджеры контекста.

person zefciu    schedule 10.06.2010

Еще один пример поддержки "из коробки", который может сначала сбить с толку, когда вы привыкли к поведению встроенного 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, он закрывается, освобождая любой ресурс, в конечном итоге связанный с ним. На состояние транзакции это не влияет.

person bgse    schedule 28.08.2017

В Python обычно оператор «with» используется для открытия файла, обработки данных, имеющихся в файле, а также для закрытия файла без вызова метода close (). Оператор with упрощает обработку исключений, обеспечивая действия по очистке.

Общая форма с:

with open(“file name”, “mode”) as file-var:
    processing statements

примечание: не нужно закрывать файл, вызывая close () после file-var.close ()

person Tushar.PUCSD    schedule 13.02.2018

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

пример:

function1(<complex and deep expression>)
function2(<same complex and deep expression>)

можно переписать как:

expr = <complex and deep expression>
function1(expr)
function1(expr)
expr = None   # to delete all resources, or wait till end of function

or as:

with <complex and deep expression> as expr:
  function1(expr)
  function1(expr)

# expr is out of scope, hence any resources are deleted
person MikeW    schedule 20.05.2021