Python enter / exit vs init (или новый) / del
я искал, и я не могу придумать какой-либо веской причины использовать python __enter__
/__exit__
, а не __init__
(или __new__
?) / __del__
.
я понимаю, что __enter__
/ __exit__
предназначены для использования с with
заявление в качестве контекстных менеджеров и with
утверждение великого. Но аналогом этого является то, что любой код в этих блоках является только выполнены в этом контексте. Используя их вместо __init__
/ __del__
кажется, я создание неявного контракта с вызывающими абонентами, которые они должны использовать with
, но нет никакого способа обеспечить соблюдение такого контракта, и контракт передается только через документацию (или чтение кода). Это кажется плохой идеей.
кажется, я получаю тот же эффект, используя __init__
/ __del__
внутри with
блок. Но, используя их, а не методы управления контекстом, мой объект также полезен в других сценариях.
так может кто-нибудь придумать убедительный причина, почему я бы когда-нибудь хотите использовать методы управления контекстом, а не методы конструктора/деструктора?
если есть лучшее место, чтобы задать такой вопрос, пожалуйста, дайте мне знать, но кажется, что там не так много хорошей информации об этом.
Дальнейшие Действия:
этот вопрос был основан на плохом (но, вероятно, распространенном) предположении, потому что я всегда использовал with
создать новый объект, в таком случае __init__/__del__
очень близко к тому же поведению, что и __enter__/__exit__
(кроме того, что вы не можете контролировать, когда или если __del__
будет выполнен, это зависит от сборки мусора, и если процесс будет завершен первым, он никогда не будет вызван). Но если вы используете существующие объекты в with
заявления они, конечно, совсем другой.
3 ответов
есть несколько различий, которые вы, кажется, пропустили:
Context manager получает возможность предоставить новый объект только для блока, который вы выполняете. Некоторые менеджеры контекста просто возвращают
self
там (как и файловые объекты), но, например, объекты подключения к базе данных могут возвращать объект курсора, привязанный к текущей транзакции.менеджеры контекста не только уведомляются о завершении контекста, но и если выход был вызвано исключение. Затем он может принять решение об обработке этого события или иначе реагировать по-разному во время выхода. Опять же, используя подключение к базе данных в качестве примера, на основе исключения можно зафиксировать или прервать транзакцию.
__del__
вызывается только, когда все ссылки на объект будут удалены. Это означает, что вы не можете полагаться на его вызов, если вам нужно иметь несколько ссылок на него, которые вы можете или не можете контролировать жизни. Однако выход context manager точно определен.-
контекстные менеджеры могут быть повторно, и они могут сохранить государство. Соединение с базой данных снова; вы создаете его один раз, затем снова и снова используете его в качестве менеджера контекста, и он будет держать это соединение открытым. Для этого нет необходимости каждый раз создавать новый объект.
это важно для блокировки потоков, например; вы есть сохранить состояние так, чтобы только один резьба может держать замок одновременно. Вы делаете это, создавая один заблокировать объект, затем использовать
with lock:
таким образом, разные потоки, выполняющие этот раздел, могут быть сделаны, чтобы ждать перед входом в этот контекст.
на __enter__
и __exit__
методы формирования контекст менеджер протоколом, и вы должны использовать их, только если вы действительно хотите управлять контекстом. Цель контекстных менеджеров-упростить common try...finally
и try...except
шаблоны, не управление временем жизни одного экземпляра. См.PEP 343 -"С" заявление:
этот PEP добавляет новый оператор " с " на язык Python, чтобы можно было исключить стандартное использование операторов try/finally.
del x
не вызывает напрямую x.__del__()
вы не можете контролировать, когда .__del__
называется, или на самом деле будет ли он вызван вообще.
__init__
/__del__
для управления контекстом не является надежным.используя их вместо
__init__
/__del__
Кажется, я создаю неявный контракт с абонентами, которые они должны использоватьwith
, но нет никакого способа исполнения такого договора
у вас есть договор в любом случае. Если пользователи используют ваш объект, не понимая, что он требует очистки после использования, они испортят все, независимо от того, как вы реализуете очистку. Они могут навсегда сохранить ссылку на ваш объект, например, предотвращая __del__
от бегущий.
если у вас есть объект, требующий специальной очистки, вы должны сделать это требование явным. Вам нужно дать пользователям with
функциональность и явную close
или аналогичный метод, чтобы позволить пользователям контролировать, когда происходит очистка. Вы не можете скрыть требование очистки внутри __del__
метод. Возможно, вы захотите реализовать __del__
тоже, как мера безопасности, но вы не можете использовать __del__
на месте with
или явный close
.
С учетом сказанного, Python не обещает, что __del__
будет работать, никогда. Стандартная реализация будет работать __del__
когда refcount объекта падает до 0, но это может не произойти, если ссылка сохраняется до конца сценария или если объект находится в цикле ссылок. Другие реализации не используют refcounting, making __del__
еще менее предсказуемой.