Попытка понять python с помощью операторов и контекстных менеджеров

Я новичок в этом и просто пытаюсь понять инструкцию with. Я понимаю, что он должен заменить блок try/except.

Теперь предположим, что я делаю что-то вроде этого:

try:
   name='rubicon'/2 # to raise an exception
except Exception as e:
   print "no not possible"
finally:
   print "Ok I caught you"

Как это заменить менеджером контекста?

Ответы

Ответ 1

with на самом деле не заменяет try/except, а скорее try/finally. Тем не менее, вы можете сделать, чтобы менеджер контекста делал что-то другое в случаях исключения из исключающих:

class Mgr(object):
    def __enter__(self): pass
    def __exit__(self, ext, exv, trb):
        if ext is not None: print "no not possible"
        print "OK I caught you"
        return True

with Mgr():
    name='rubicon'/2 #to raise an exception

Часть return True - это то, где менеджер контекста решает исключить исключение (как вы это делаете, не увеличивая его в своем предложении except).

Ответ 2

contextlib.contextmanager функция decorator обеспечивает удобный способ предоставления менеджера контекста без необходимости писать полный -fledged ContextManager (с методами __enter__ и __exit__, поэтому вам не нужно запоминать аргументы метода __exit__ или что метод __exit__ должен return True в порядке для исключения исключения). Вместо этого вы пишете функцию с одним yield в той точке, в которой вы хотите, чтобы блок with запускался, и вы улавливаете любые исключения (которые эффективно исходят от yield), как обычно.

from contextlib import contextmanager
@contextmanager
def handler():
    # Put here what would ordinarily go in the `__enter__` method
    # In this case, there nothing to do
    try:
        yield # You can return something if you want, that gets picked up in the 'as'
    except Exception as e:
        print "no not possible"
    finally:
        print "Ok I caught you"

with handler():
    name='rubicon'/2 #to raise an exception

Зачем переходить к дополнительной проблеме написания менеджера контекста? Повторное использование кода. Вы можете использовать один и тот же менеджер контекста в нескольких местах, не повторяя обработку исключений. Если обработка исключений уникальна для этой ситуации, то не беспокойтесь с помощью диспетчера контекстов. Но если один и тот же шаблон появляется снова и снова (или, если это может быть для ваших пользователей, например, закрытие файла, разблокировка мьютекса), это стоит дополнительных проблем. Это также аккуратный шаблон для использования, если обработка исключений немного сложна, поскольку она отделяет обработку исключений от основной строки потока кода.

Ответ 3

with в Python предназначен для упаковки набора инструкций, где вы должны настроить и уничтожить или закрыть ресурсы. Это аналогично try...finally в этом отношении, поскольку предложение finally будет выполняться даже после исключения.

Диспетчер контекста - это объект, реализующий два метода: __enter__ и __exit__. Они вызываются непосредственно до и после (соответственно) блока with.

Например, посмотрите на классический пример open():

with open('temp.txt', 'w') as f:
    f.write("Hi!")

Open возвращает объект File, который реализует __enter__ более или менее как return self и __exit__ как self.close().

Ответ 4

Компоненты контекстного менеджера

  1. Вы должны реализовать метод __enter__, который возвращает объект
  2. Реализуйте метод __exit__.

Пример

Я приведу простой пример, чтобы показать вам, зачем нам нужен контекстный менеджер. Зимой в Синьцзяне, Китай, вы должны немедленно закрывать дверь, когда открываете дверь. если вы забудете закрыть его, вам станет холодно.

 class Door:
     def __init__(self):
         self.doorstatus='the door was closed when you are not at home'
         print(self.doorstatus)
     def __enter__(self):
         print('I have opened the door')
         return self
     def __exit__(self,*args):
         print('pong!the door has closed')
     def fetchsomethings(self):
         print('I have fetched somethings')

когда приносишь вещи домой, открой дверь, принеси что-нибудь и закрой дверь.

 with Door() as dr:
     dr.fetchsomethings()

вывод:

the door was closed when you are not at home
I have opened the door
I have fetched somethings
pong!the door has closed

Объяснение

когда вы инициируете класс Door, он вызовет __init__ метод, который будет печатать "дверь была закрыта, когда вас нет дома" и __enter__ метод, который будет печатать "Я открыл дверь" и возвращать экземпляр двери с именем dr. при вызове self.fetchsomethings с блоком метод выведет "Я кое-что получил". Когда блок завершится. Менеджер контекста вызовет __exit__ метод, и он напечатает "Понг! Дверь закрылась". Когда вы не используете с __enter__ и __exit__ не будут задействованы !!!!

Ответ 5

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

Допустим, вы открыли файл для записи:

f = open(path, "w")

Теперь у вас есть дескриптор открытого файла. Во время обработки вашего файла никакая другая программа не может писать в него. Чтобы позволить другим программам писать в него, вы должны закрыть дескриптор файла:

f.close()

Но перед закрытием файла произошла ошибка:

f = open(path, "w")
data = 3/0  # Tried dividing by zero. Raised ZeroDivisionError
f.write(data)
f.close()

Что произойдет сейчас, так это то, что функция или вся программа завершат работу, оставив ваш файл с открытым дескриптором. (CPython очищает дескрипторы при завершении и дескрипторы освобождаются вместе с программой, но вы не должны на это рассчитывать)

Оператор with гарантирует, что, как только вы оставите отступ, он закроет дескриптор файла:

with open(path, "w") as f:
    data = 3/0  # Tried dividing by zero. Raised ZeroDivisionError
    f.write(data)
# In here the file is already closed automatically, no matter what happened.

Заявления with могут использоваться для многих других вещей. Например: threading.Lock()

lock = threading.Lock()
with lock:  # Lock is acquired
   do stuff...
# Lock is automatically released.

Почти все, что делается с помощью диспетчера контекста, может быть выполнено с помощью try: ... finally: ..., но контекстные менеджеры удобнее в использовании, удобнее, удобочитаемее и благодаря реализации __enter__ и __exit__ предоставляют простой в использовании интерфейс.


Создание менеджеров контекста выполняется путем внедрения __enter__() и __exit__() в обычный класс.

__enter__() говорит, что делать, когда менеджер контекста запускается, и __exit__(), когда менеджер контекста существует (давая исключение методу __exit__(), если произошло исключение)

Ярлык для создания менеджеров контекста можно найти в contextlib. Он оборачивает генератор как менеджер контекста.