Попытка понять 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)
-
[параметры в этом методе используются для управления исключениями]