试图用语句和上下文管理器来理解python

时间:2010-09-12 04:40:02

标签: python contextmanager

我是新手,我只是想了解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"

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

5 个答案:

答案 0 :(得分:23)

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子句中没有重新提升它一样)。

答案 1 :(得分:18)

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

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

答案 2 :(得分:14)

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

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

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

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

Open会返回一个File对象,该对象实现__enter__或多或少像return self__exit__,如self.close()

答案 3 :(得分:6)

上下文管理器的组件

1.您应该实现一个返回对象的 __ enter __ 方法 2.实现 __退出__ 方法

例如

我将举一个简单的例子向您展示我们为什么需要上下文管理器。在中国新疆的冬天,当您打开门时,应立即关上门。如果您忘记了 关闭它,你会得到***。 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 __ 方法 "当你不在家时,门被关闭了#34;和 __输入__ 方法将打印"我打开了大门"并返回一个名为dr的门实例。当使用块调用 self.fetchsomethings 时,该方法将打印"我已经取出了某些东西"。当块完成时。上下文管理器将调用 __ exit __ 方法,它将打印" pong!门关闭"当你不使用时 关键字, __输入__ __退出__ 将不会被调用!!!!

答案 4 :(得分: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__和{{ 1}}提供易于使用的界面。

通过在普通班级中实施__enter__()__exit__()来创建上下文管理器。

__exit__告诉上下文管理器启动时要执行的操作以及上下文管理器存在时__enter__()(如果发生异常,则为__exit__()方法提供异常)

可以在contextlib中找到创建上下文管理器的快捷方式。它将生成器包装为上下文管理器。