Вопрос

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

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

Решение

  1. Я считаю, что это уже ответило другим пользователям перед собой, поэтому я только добавляю его ради полноты: with Заявление упрощает обработку исключения путем инкапсулирования общих задач препарата и очистки в так называемых контекстные менеджеры. Отказ Дополнительные детали можно найти в PEP 343.. Отказ Например, open Заявление - это менеджер контекста сам по себе, который позволяет открыть файл, держать его открытым, пока выполнение находится в контексте with Заявление, где вы использовали его, и закройте его, как только вы оставите контекст, независимо от того, оставили ли вы его из-за исключения или во время регулярного контрольного потока. То with Таким образом, заявление может использоваться способом, похожим на Raii Pattern в 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
    

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top