Python __enter__/__exit__ vs __init __(或__new__)/ __del__

Bob*_*tle 18 python constructor destructor with-statement contextmanager

我已经搜索过了,我无法想出任何使用python的__enter__/ __exit__而不是__init__(或__new__?)/的理由__del__.

我理解__enter__//__exit__旨在与with语句一起用作上下文管理器,with声明很棒.但与此相对应的是,这些块中的任何代码在该上下文中执行.通过使用这些而不是__init__/ __del__我似乎与他们必须使用的调用者创建隐式契约with,但是没有办法强制执行这样的契约,并且合同只通过文档(或阅读代码)传达.这似乎是一个坏主意.

我似乎使用__init__/ __del__with块内部获得相同的效果.但是通过使用它们而不是上下文管理方法,我的对象在其他场景中也很有用.

因此,任何人可以想出为什么我会一个令人信服的理由以往任何时候都需要使用上下文的管理方法,而不是构造函数/析构函数的方法呢?

如果有更好的地方可以提出这样的问题,请告诉我,但似乎没有太多关于此的信息.

跟进:

这个问题是基于一个糟糕的(但可能是常见的)假设,因为我总是习惯于with实例化一个新对象,在这种情况下__init__/__del__非常接近于相同的行为__enter__/__exit__(除非您无法控制何时或是否__del__将被执行,它是直到垃圾收集,如果进程终止,它可能永远不会被调用).但是如果你在with语句中使用预先存在的对象,它们当然是完全不同的.

Mar*_*ers 14

您似乎错过了一些差异:

  • 上下文管理器有机会为您正在执行的块提供新对象.一些上下文管理器只返回self那里(就像文件对象那样),但是,作为一个例子,数据库连接对象可以返回绑定到当前事务的游标对象.

  • 上下文管理器不仅会通知上下文结束,还会通知退出是由异常引起的.然后,它可以决定处理该事件或在退出期间以不同方式作出反应.再次使用数据库连接作为示例,基于存在异常,您可以提交或中止事务.

  • __del__仅在删除对对象的所有引用时调用.这意味着如果你需要对它进行多次引用而你可能控制或不控制它的生命周期,你就不能依赖它.然而,精确定义了上下文管理器出口.

  • 上下文管理器可以重用,并且可以保持状态.数据库连接再次; 你创建它一次,然后一次又一次地使用它作为上下文管理器,它将保持该连接打开.每次都不需要为此创建新对象.

    例如,这对于线程锁很重要; 你必须保持状态,以便一次只有一个线程可以保持锁定.您可以通过创建一个锁对象来执行此操作,然后使用with lock:执行该节的不同线程,每个线程都可以在进入该上下文之前等待.

__enter____exit__方法形成上下文管理协议,并且你应该只使用这些,如果你真的要管理的上下文.上下文管理器的目标是简化常见try...finallytry...except模式,而不是管理单个实例的生命周期.参见PEP 343 - "with"声明:

这个PEP在Python语言中添加了一个新的"with"语句,可以分解try/finally语句的标准用法.

  • @BobDoolittle:实现`__enter__`和`__exit__`**没有**'with`语句没什么意义.`with`语句是我们首先使用这些方法的原因. (2认同)

wim*_*wim 6

del x 不直接调用 x.__del__()

您无法控制何时.__del__被调用,或者实际上是否完全被调用

因此,使用__init__/__del__进行上下文管理是不可靠的。

  • @BobDoolittle:`__init__` 是为了初始化,所以如果你想做初始化,就在那里做。`__enter__` 用于在输入 `with` 语句时应该发生的工作;例如,`__enter__` 可能会锁定一个锁,而`__exit__` 可能会解锁它。 (3认同)