Для чего предназначен оператор python "with"? - PullRequest
380 голосов
/ 10 июня 2010

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

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

Ответы [ 10 ]

363 голосов
/ 10 июня 2010
  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
    
85 голосов
/ 10 июня 2010

Я бы предложил две интересные лекции:

  • PEP 343 Заявление "with"
  • Effbot Понимание Python "with"оператор

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. Я не вижу здесь никакого Антипаттерна.
Цитирование Погружение в Python :

try..finally - это хорошо.с лучше.

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

36 голосов
/ 10 июня 2010

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

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

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

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

25 голосов
/ 10 июня 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

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

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

9 голосов
/ 10 июня 2010

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

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

4 голосов
/ 10 июня 2010

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

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

3 голосов
/ 10 июня 2010

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

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

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

2 голосов
/ 13 февраля 2018

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

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

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

note: нет необходимости закрывать файл, вызываяclose () для файла-var.close ()

2 голосов
/ 29 августа 2017

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

...