Попытка понять 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"

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


person user444997    schedule 12.09.2010    source источник
comment
Контекстный менеджер заменяет только блок try/finally.   -  person N Randhawa    schedule 27.08.2016


Ответы (5)


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

person Alex Martelli    schedule 12.09.2010

Декоратор функции contextlib.contextmanager предоставляет удобный способ предоставления менеджера контекста без необходимости писать собственный полноценный класс 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's 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

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

person Paul Price    schedule 01.08.2013

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

person Pablo Alejandro Costesich    schedule 12.09.2010

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

  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 в блоке метод напечатает «Я получил что-то». напечатайте "понг! дверь закрылась". Если вы не используете ключевое слово , __enter__ и __exit__ не будут вызываться!!!!

person 未来陆家嘴顶尖的投资人    schedule 06.03.2017

Операторы 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. Он обертывает генератор как контекстный менеджер.

person Bharel    schedule 12.04.2016