前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python 上下文管理器、 else 块、@contextmanager

python 上下文管理器、 else 块、@contextmanager

作者头像
Michael阿明
发布2021-09-06 11:59:12
2330
发布2021-09-06 11:59:12
举报
文章被收录于专栏:Michael阿明学习之路

文章目录

learn from 《流畅的python》

1. else

  • for/else、while/else 和 try/else 前两者 只有在 没有被break 时,才会运行 else try 块中没有异常抛出时 才运行 else
代码语言:javascript
复制
for i in range(3):
    print(i)
else:
    print("finish, no break")
    # finish, no break

for i in range(3):
    if i == 2:
        break
    print(i)
else:
    print("break")
    # 无输出

i = 0
while i < 3:
    print(i)
    i += 1
else:
    print("no break") # no break
    
i = 0
while i < 3:
    if i==2:
        break
    print(i)
    i += 1
else:
    print("break") # 无输出

arr = [0]
try:
    arr[0] = 1
except:
    print("error") # 无输出
else:
    print("no error, run else")
    # no error, run else

arr = []
try:
    arr[0] = 0
except:
    print("error") # error
else:
    print("error, will not run else") # 无输出
finally:
    print("finish") # finish
# finally 子句中的代 码通常用于释放重要的资源,或者还原临时变更的状态

else 这么用的比较少

2. with上下文管理器

上下文管理器协议包含 __enter____exit__ 两个方法

  • with 语句 运行时,会在上下文管理器对象上调用 __enter__ 方法
  • with 语句 结束后,会在上下文管理器对象上调用 __exit__ 方法,以此扮 演 finally 子句的角色(释放重要的资源,或者 还原临时变更的状态),如 关闭文件等
代码语言:javascript
复制
class LookingGlass:
    def __enter__(self): # 没有其它的参数了
        import sys
        self.original_write = sys.stdout.write
        sys.stdout.write = self.reverse_write
        return 'ABCD'

    def reverse_write(self, text): # 反转字符串,实现输出
        self.original_write(text[::-1])

    def __exit__(self, exc_type, exc_value, traceback):
        # exc_type 异常类
        # exc_value 异常实例
        # traceback 对象
        # 在 try/finally 语句的 finally 块中调用 sys.exc_info()
        # 得到的就是 __exit__ 接收的这三个参数
        import sys
        sys.stdout.write = self.original_write
        if exc_type is ZeroDivisionError:
            print('Please DO NOT divide by zero!')
            return True


with LookingGlass() as what:
    # 进入 __enter__ 函数时返回的 ABCD 字符串存入 what
    print("Michael learning python")
    # nohtyp gninrael leahciM
    print(what)
    # DCBA
print("Michael learning python")
# Michael learning python
print(what)
# ABCD
代码语言:javascript
复制
manager = LookingGlass()
print(manager)
# <__main__.LookingGlass object at 0x00000231226F2190>
string = manager.__enter__()
print(string)  # DCBA
print(string == "ABCD")  # eurT, 所有的输出经过管理器的 反向输出处理了
print(string == "DCBA")  # eslaF, 所有的输出经过管理器的 反向输出处理了
print(manager)
# >0912F62213200000x0 ta tcejbo ssalGgnikooL.__niam__<
manager.__exit__(None, None, None) # 还原了正常的输出
print(string)  # ABCD

3. contextlib模块实用工具

  • closing 如果对象 提供了 close() 方法,但没有实现 __enter__/__exit__ 协议,那么可以使用这个函数构建上下文管理器
  • suppress, 构建临时 忽略指定异常 的上下文管理器
  • @contextmanager,装饰器 把简单的 生成器函数 变成 上下文管理器,这样就不用创建类去实现管理器协议了
  • ContextDecorator,这是个基类,用于定义基于类的上下文管理器。这种上下文管理器 也能用于 装饰函数,在受管理的上下文中运行整个函数
  • ExitStack,这个上下文管理器 能进入多个 上下文管理器。with 块结束时,ExitStack 按照后进先出的顺序调用栈中各个上下文管理器的 __exit__ 方法。 如果事先不知道 with 块要进入 多少个上下文管理 器,可以使用这个类。例如,同时打开任意一个文件列表中的所有文件

使用最广泛的是 @contextmanager 装饰 器,因此要格外留心。 这个装饰器也有迷惑人的一面,因为它与迭代 无关,却要 使用 yield 语句

4. @contextmanager 装饰器

  • @contextmanager 装饰器能减少代码量,因为 不用编写一个完整的类,定义 __enter__ 和 __exit__ 方法,而 只需实现 有一个 yield 语句的生成器,生成想让 __enter__ 方法返回的值
  • 在使用 @contextmanager 装饰的生成器中,yield 语句的作用是 把函数的定义体分成两部分: yield 语句前面的所有代码在 with 块开始时 (即解释器调用 __enter__ 方法时)执行 yield 语句后面的代码在 with 块结束时(即调用 __exit__ 方法时)执行
代码语言:javascript
复制
import contextlib


@contextlib.contextmanager
def looking_glass1():
    import sys
    original_write = sys.stdout.write

    def reverse_write(text):
        original_write(text[::-1])

    sys.stdout.write = reverse_write
    yield 'ABCD'  # 产出的值绑定到 as 目标上
    # 执行 with 块代码时,函数会在这里暂停
    sys.stdout.write = original_write
    #  控制权一旦跳出 with 块,继续执行 yield 语句之后的代码


with looking_glass1() as what:
    print("Michael learning python")
    print(what)
# nohtyp gninrael leahciM
# DCBA
print("Michael learning python")
print(what)
# Michael learning python
# ABCD

其实,contextlib.contextmanager 装饰器会把函数包装成实现 __enter__ 和 __exit__ 方法的

用于原地重写文件的上下文管理器

代码语言:javascript
复制
import csv 
with inplace(csvfilename, 'r', newline='') as (infh, outfh): 
	reader = csv.reader(infh) 
	writer = csv.writer(outfh) 

	for row in reader: 
		row += ['new', 'columns'] 
		writer.writerow(row)

inplace 函数是个上下文管理器,为同一个文件提供了两个句柄(例中的 infh 和 outfh),以便同时读写同一个文件。比标准库中的 fileinput.input 函数 易用

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/08/26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1. else
  • 2. with上下文管理器
  • 3. contextlib模块实用工具
  • 4. @contextmanager 装饰器
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档