24

我试图理解该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.")

如何用上下文管理器替换它?

4

5 回答 5

30

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子句中不重新提出它一样)。

于 2010-09-12T04:46:40.130 回答
27

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

为什么要费心编写上下文管理器?代码重用。您可以在多个地方使用相同的上下文管理器,而不必重复异常处理。如果异常处理是该情况所特有的,那么不要打扰上下文管理器。但是,如果相同的模式一次又一次地出现(或者如果它可能对您的用户来说,例如,关闭文件、解锁互斥锁),那么额外的麻烦是值得的。如果异常处理有点复杂,这也是一种简洁的模式,因为它将异常处理与代码流的主线分开。

于 2013-08-01T19:48:18.633 回答
15

Python 中的with旨在包装一组语句,您应该在其中设置和销毁或关闭资源。在这方面,它的方式类似于try...finallyfinally 子句,即使在异常之后也会执行。

上下文管理器是一个实现两种方法的对象:__enter____exit__. 这些在块之前和之后(分别)被调用with

例如,看一下经典open()示例:

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

Open 返回一个或多或少File实现like和like的对象。__enter__return self__exit__self.close()

于 2010-09-12T04:47:15.027 回答
9

上下文管理器的组件

  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 的门实例。当用 block 调用self.fetchsomethings时,该方法将打印“I have fetchsomethings”。当块完成时。上下文管理器将调用__exit__ 方法并打印“pong!门已关闭”。当你不这样做时与关键字一起使用,__enter____exit__将不会被调用!!!!

于 2017-03-06T10:42:24.290 回答
5

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 中找到创建上下文管理器的快捷方式。它将生成器包装为上下文管理器。

于 2016-04-12T06:42:47.353 回答