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

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

6 ответов

Решение

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 пункт).

Декоратор функций 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

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

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

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

Например, взгляните на классический open() пример:

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

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

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

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

пример

Я приведу простой пример, чтобы показать вам, почему нам нужен менеджер контекста. В течение зимы в Синьцзяне, Китай, вы должны немедленно закрыть дверь, когда откроете дверь. Если вы забудете закрыть ее, вы получите ***. class Door: def __init__(self): self.doorstatus='the door was closed when you are not in 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 in home i have opened the door i have fetched somethings pong!the door has closed

Explation

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

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. Это оборачивает генератор как менеджер контекста.

Управление ресурсами. В любом языке программирования очень распространено использование таких ресурсов, как операции с файлами или соединения с базой данных. Но эти ресурсы ограничены в предложении. Поэтому основная проблема заключается в том, чтобы освободить эти ресурсы после использования. Если они не будут выпущены, это приведет к утечке ресурсов и может привести к замедлению или сбою системы. Было бы очень полезно, если бы у пользователей был механизм автоматической установки и отключения ресурсов. В Python это может быть достигнуто за счет использования менеджеров контекста, которые облегчают правильную обработку ресурсов. Наиболее распространенный способ выполнения операций с файлами — использование ключевого слова, как показано ниже:

       # Python program showing a use of with keyword
 with open("test.txt") as f:  
     data = f.read()

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

      file_descriptors = []
for x in range(100000):
    file_descriptors.append(open('test.txt', 'w'))

это приводит к ошибке:OSError: [Errno 24] Too many open files: 'test.txt'

Python предоставляет простой способ управления ресурсами: менеджеры контекста. Используется ключевое слово with. Когда он оценивается, он должен привести к объекту, который выполняет управление контекстом. Контекстные менеджеры могут быть написаны с использованием классов или функций (с декораторами).

Создание контекстного менеджера: при создании контекстных менеджеров с использованием классов пользователь должен убедиться, что класс имеет методы: и . Возвращает ресурс, которым нужно управлять, и ничего не возвращает, но выполняет операции очистки. Во-первых, давайте создадим простой класс с именем ContextManager, чтобы понять базовую структуру создания контекстных менеджеров с использованием классов, как показано ниже:

      # Python program creating a context manager 
class ContextManager():
    def __init__(self):
        print('init method called')
         
    def __enter__(self):
        print('enter method called')
        return self
     
    def __exit__(self, exc_type, exc_value, exc_traceback):
        print('exit method called')
 
with ContextManager() as manager:
    print('with statement block')

Output:
init method called
enter method called
with statement block
exit method called

В этом случае создается объект ContextManager. Это присваивается переменной после ключевого слова т.е. менеджер. При запуске вышеуказанной программы последовательно выполняются следующие действия:

  • __init__()
  • __enter__()
  • тело оператора (код внутри блока with)
  • [параметры в этом методе используются для управления исключениями]
Другие вопросы по тегам