Что такое питон "с", предназначенный для?

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

  • Что такое оператор Python with предназначен для использования?
  • Что делать вы используете его для?
  • Есть ли какие-либо Мне нужно знать, или общие анти-шаблоны, связанные с его использование? Любые случаи, когда лучше использовать try..finally чем with?
  • Почему он не используется более широко?
  • Какие стандартные классы библиотек совместимы с ним?

Ответ 1

  • Я считаю, что на меня уже ответили другие пользователи, поэтому я только добавляю их для полноты: инструкция with упрощает обработку исключений путем инкапсуляции общих задач подготовки и очистки в так называемом контекстные менеджеры. Более подробную информацию можно найти в PEP 343. Например, оператор open представляет собой менеджер контекста сам по себе, который позволяет открывать файл, держать его открытым, пока выполнение выполняется в контексте оператора with, где вы его использовали, и закрывайте его как можно скорее когда вы покидаете контекст, независимо от того, покинули ли вы его из-за исключения или во время регулярного потока управления. Таким образом, оператор with можно использовать способами, аналогичными образу RAII на С++: некоторый ресурс приобретается оператором 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
    

Ответ 2

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

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:

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

Когда a cursor выходит из with-block, он закрывается, освобождая любой ресурс, в конечном счете связанный с ним. Состояние транзакции не влияет.

Ответ 10

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

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

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

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

Ответ 11

Все возможные решения перечислены в приведенных выше ответах.

Я использую исчерпывающие ключевые слова для читсов:

Keywords_33=[('File_2', ['with', 'as']),
             ('Module_2', ['from', 'import']),
             ('Constant_3', {'bool': ['False', 'True'],
                             'none': ['None']}),
             ('Operator_4', {'boolean_operation': {'or', 'and', 'not'},
                             'comparison': {'is'}}),
             ('Sequnce_operation_2', ['in', 'del']),
             ('Klass_1', ['class']),
             ('Function_7',['lambda', 'def', 'pass',
                            'global', 'nonlocal',
                            'return', 'yield']),
             ('Repetition_4', ['while', 'for', 'continue', 'break']),
             ('Condition_3', ['if', 'elif', 'else']),
             ('Debug_2', ['assert', 'raise']),
             ('Exception_3', ['try', 'except', 'finally'])]