with语句还能这么用,你会吗?——with语句和上下文管理器

启程

   with 语句究竟有哪些好处?它有助于简化一些通用资源管理模式,抽象出其中的功能,将其分解并重用。

  若想充分的使用这个特性,比较好的办法是查看Python标准库的示例。内置的 open()函数就是一个很好的例子:

with open('hello.txt', 'w') as f:
	f.write('hello, world!')

我们应该都知道打开文件的时候一般建议使用 with 语句,因为这样能确保打开的文件描述符在程序执行离开 with语句 的上下文后自动关闭。本质上来说,上面的代码示例可转换成下面这样:

f = open('hello.txt', 'w')
try:
    f.write('hello, world')
finally:
    f.close()

很明显,这段代码比with语句冗长。 注意,当中的try……finally 语句也很重要,只关注其中的逻辑代码还不够:

f = open('hello.txt', 'w')
f.write('hello, world')
f.close()

如果在调用f.write()时发生异常,这段代码不能保证文件最后被关闭,因此程序可能会泄露文件描述符。此时with语句就派上用场了,它能够简化资源的获取和释放。

threading.Lock类是python标准库中另一个比较好的示例,它有效地使用了with语句

some_loke = threading.Lock()

#常见版:
some_lock.acquire()
try:
    #执行某些操作.....
finally:
    some_loke.release()

#改进版:
with some_loke:
    #执行某些操作.....

在这两个例子中,使用with语句都可以抽象出大部分资源处理逻辑。不必每次都显示地写一个try……finally语句with语句会自行处理。
with 语句不仅让处理系统资源的代码更易读,而且由于绝对不会忘记清理获释放资源,因此还可以避免 bug或资源泄露。

下面进行with语句的剖析,在自定义对象中支持with

   无论是 open()函数threading.Lock类本身,还是它们与with语句一起使用,这些都是没有什么特殊之处。只要实现所谓的上下文管理器(context manager),就可以在自定义的类和函数中获得相同的功能。

对了,那上下文管理器是个什么鬼?这是一个简单的协议(或接口),自定义对象需要遵循这个接口来支持with语句。总的来说,如果想将一个对象作为上下文管理器,需要做的就是向其中添加两个方法:__enter__和__exit__方法。python将在资源管理周期的适当时间调用这两种方法。

来看看实际代码,下面是open()上下文管理器的一个简单实现:

class ManagedFile:
    def __init__(self, name):
        self.name = name
    def __enter__(self):
        self.file = open(self.name, 'w')
        return self.file
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()

其中的ManagedFile类遵循上下文管理器协议,所以与原来的open()例子一样,也支持with语句

with ManagedFile('hello.txt') as f:
    f.write('hello, world!')
    f.write('bey now')

当执行流程进入with语句上下文时,Python会调用__enter__获取资源;离开with上下文时,Python会调用__exit__释放资源。

进阶

如果想要管理的“资源”是某种报告生成程序中的文本缩进层次,可以编写下面这样的代码:

with Indenter() as indent:
    indent.print('Hi')
    with indent:
        indent.print('hello')
        with indent:
            indent.print('quanluo')
    indent.print('Hey')

这些语句读起来有点像用于缩进文本的领域特定语言(DSL)。注意这段代码多次进入离开相同的文本管理器,以此来更改缩进级别。运行这段代码会在控制台中整齐地显示出下面的内容:

  Hi
    hello
      quanluo
  Hey

那么如何实现一个上下文管理器来支持这种功能呢?
这是一个不错的练习,可以从中准确的了解上下文管理器的工作方式。因此在查看下面的实现之前,最好先花一些时间尝试自行实现。

如果你已经准备好查看实现代码了,那么下面就是使用基于类的上下文管理器来实现的方法:

class Indenter:
    def __init__(self):
        self.level = 0
    def __enter__(self):
        self.level += 1
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.level -= 1
    def print(self, text):
        print('  ' * self.level + text)

感觉怎么样,和你自己想的有差别吗?
如果还不错,希望你现在能熟练地在自己的Python程序中使用上下文管理器with语句
这两个功能很不错,可以用来以更加有Python特色和可维护的方式处理资源管理问题。

加深理解

如果你还想再找一个练习来加深理解,可以尝试实现一个使用time.time()函数来测量代码块的执行时间的上下文管理器
示例代码:

with times():
    count = 0
    for i in range(1, 1000001):
            count += i
    print(count)

运行这段代码会在命令行中显示下面的内容:

500000500000
程序运行时间为:0.15491032600402832s

请自行编写实现该times类的代码。

探索

with语句也能用来与装饰器连用,可以自行在自己Python程序上试试.


注: 如果想要关于实现times类的源代码请在下方评论区告诉我。我看见了会发给你。


🔗原文链接:https://blog.csdn.net/weixin_43347550/article/details/105968879

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章