python笔记64 - with语法(__enter__和__exit__)

前言

with 语句适用于对资源进行访问的场景,在使用过程中如果发生异常需执行“清理”操作释放资源,比如常用的场景是with open打开文件操作。

with 打开文件场景

我们接触的第一个使用with的场景是用open函数对文件的读写操作,下面的代码是打开文件读取文件内容后用close关闭

fp = open('1.txt', 'r')
r = fp.read()
print(r)
fp.close()

上面的代码会有2个弊端:
1.如果在打开文件操作的过程中fp.read()出现异常,异常没处理
2.很多同学没用使用fp.close()关闭的操作的习惯,导致资源一直不会释放
所以推荐用下面这种with语法

with open('1.txt', 'r') as fp:
r = fp.read()
print(r)

学到这里我们都是死记硬背,只知道用with有这么2个好处,但不知道为什么这么用,也不知道其它的场景能不能用with?

with 使用原理

上下文管理协议(Context Management Protocol):包含方法 __enter__()__exit__(),支持该协议的对象要实现这两个方法。
上下文管理器(Context Manager):
支持上下文管理协议的对象,这种对象实现了__enter__()__exit__()方法。上下文管理器定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。
通常使用with语句调用上下文管理器,也可以通过直接调用其方法来使用。

with语句需当一个新的语法去学习,使用语法格式

with EXPR as Variable:
BLOCK

其中EXPR可以是任意表达式;as Variable是可选的,as的作用类似于=赋值。其一般的执行过程是这样的:

  • 执行EXPR,生成上下文管理器context_manager;

  • 获取上下文管理器的__exit()__方法,并保存起来用于之后的调用;

  • 调用上下文管理器的__enter__()方法;

    如果使用了as子句,则将__enter__()方法的返回值赋值给as子句中的Variable;

  • 执行 BLOCK 中的代码块;

先看一个示例,使用with语法的时候,先调用__enter__方法,再执行 BLOCK 代码块,最后调用__exit__退出代码

class WithDemo(object):
def __init__(self):
self.name = "yoyo"

def start(self):
print("start")

def __enter__(self):
print("enter---")

def __exit__(self, exc_type, exc_val, exc_tb):
print("quit 退出代码")

with WithDemo() as a:
print("11")

运行结果

enter---
11
quit 退出代码

不管是否执行过程中是否发生了异常,执行上下文管理器的__exit__()方法,__exit__()方法负责执行“清理”工作,如释放资源等。
出现异常时,如果`__exit__
`(self, exc_type, exc_val, exc_tb)返回False,则会重新抛出异常,让with之外的语句逻辑来处理异常,这也是通用做法;

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class WithDemo(object):
def __init__(self):
self.name = "yoyo"

def start(self):
print("start")
return self

def __enter__(self):
print("enter---")

def __exit__(self, exc_type, exc_val, exc_tb):
print("quit 退出代码")
return False

with WithDemo() as a:
print("11")
a.start("a")

调用start方法出现异常,也会执行__exit__,此方法返回False,于是就会抛出异常

enter---
11
quit 退出代码
Traceback (most recent call last):
File "D:/wangyiyun_hrun3/demo/c.py", line 18, in <module>
a.start("a")
AttributeError: 'NoneType' object has no attribute 'start'

如果返回True,则忽略异常,不再对异常进行处理。

def __exit__(self, exc_type, exc_val, exc_tb):
print("quit 退出代码")
return True

返回True的时候,不会抛出异常

allure.step使用场景

在使用pytest+allure报告的时候,经常会看到在用例里面使用with allure.step方法

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

import pytest
import allure
import requests

@pytest.fixture(scope="session")
def session():
s = requests.session()
yield s
s.close()

def test_sp(s):
with allure.step("step1:登录"):
s.post('/login', json={})
with allure.step("step2:添加商品"):
s.post('/goods', json={})
with allure.step("step3:查询商品id"):
s.get('/gooods/1')
assert 1 == 1

可以看下allure.step()方法的源码

def step(title):
if callable(title):
return StepContext(title.__name__, {})(title)
else:
return StepContext(title, {})

返回的是StepContext类的实例对象

class StepContext:

def __init__(self, title, params):
self.title = title
self.params = params
self.uuid = uuid4()

def __enter__(self):
plugin_manager.hook.start_step(uuid=self.uuid, title=self.title, params=self.params)

def __exit__(self, exc_type, exc_val, exc_tb):
plugin_manager.hook.stop_step(uuid=self.uuid, title=self.title, exc_type=exc_type, exc_val=exc_val,
exc_tb=exc_tb)

在StepContext类里面定义了__enter____exit__方法
于是有同学就想当然是不是也可以allure.title(),这个是不对的。

其它使用场景

使用python的requests模块发请求

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

import requests

s = requests.Session()
r = s.get("https://www.cnblogs.com/yoyoketang/")
print(r.status_code)
s.close()

在Session类也可以看到定义了__enter____exit__方法

class Session(SessionRedirectMixin):
"""A Requests session.

Provides cookie persistence, connection-pooling, and configuration.

Basic Usage::

>>> import requests
>>> s = requests.Session()
>>> s.get('https://httpbin.org/get')
<Response [200]>

Or as a context manager::

>>> with requests.Session() as s:
... s.get('https://httpbin.org/get')
<Response [200]>
"""

__attrs__ = [
'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify',
'cert', 'adapters', 'stream', 'trust_env',
'max_redirects',
]

def __init__(self):
......

def __enter__(self):
return self

def __exit__(self, *args):
self.close()

在源码里面是可以看到with语法的使用示例

>>> with requests.Session() as s:
... s.get('https://httpbin.org/get')
<Response [200]>

于是也可以这样写

with requests.Session() as s:
r = s.get("https://www.cnblogs.com/yoyoketang/")
print(r.status_code)

参考资料:https://www.cnblogs.com/pythonbao/p/11211347.html
参考资料:https://blog.csdn.net/u012609509/article/details/72911564

(0)

相关推荐

  • Python 的上下文管理器是怎样设计的?

    设为"星标",重磅干货,第一时间送达 花下猫语:最近,我在看 Python 3.10 版本的更新内容时,发现有一个关于上下文管理器的小更新,然后,突然发现上下文管理器的设计 PEP ...

  • python 中_ _enter_ _ 和 _ _exit_ _ 

    我们前面文章介绍了迭代器和可迭代对象,这次介绍python的上下文管理.在python中实现了__enter__和__exit__方法,即支持上下文管理器协议.上下文管理器就是支持上下文管理器协议的对 ...

  • 【python笔记】python java 语法,对比学习

    【python笔记】python java 语法,对比学习

  • python笔记55-有哪些 pythonic 的优雅语法能让你面试加分?

    前言 现在面试的时候经常会考几个 python 基础的代码题,从答题者的解答代码就很容易看出一个人的代码水平. 一般笔试考代码题,主要是考察应聘者的代码是解决的这一个问题,还是这一类的问题? 交互 如 ...

  • 完整版Python笔记|超级有用

    完整版Python笔记|超级有用

  • 每日一课 | Python的四大基本语法

    今天 02 大家好,我是营长,上期营长分享了"详解Pythonl两大特性",这期营长接着为大家分享Python相关内容. 本期分享内容:Python四大基本语法 这期分享营长邀请的 ...

  • python笔记2-冒泡排序

    前言 面试的时候经常有面试官喜欢问如何进行冒泡排序?这个问题相信能难倒一批英雄好汉,本篇就详细讲解如何用python进行冒泡排序. 一.基本原理 1.概念: 冒泡排序(Bubble Sort),是一种 ...

  • python笔记3-发送邮件(smtplib)

    前言 本篇总结了QQ邮箱和163邮箱发送邮件,邮件包含html中文和附件,可以发给多个收件人,专治各种不行,总之看完这篇麻麻再也不用担心我的邮件收不到了. 以下代码兼容python2和python3, ...

  • python笔记4-遍历文件夹目录os.walk()

    前言 如何遍历查找出某个文件夹内所有的子文件呢?并且找出某个后缀的所有文件 一.walk功能简介 1.os.walk() 方法用于通过在目录树种游走输出在目录中的文件名,向上或者向下. 2.walk( ...

  • python笔记5-python2写csv文件中文乱码问题

    前言 python2最大的坑在于中文编码问题,遇到中文报错首先加u,再各种encode.decode. 当list.tuple.dict里面有中文时,打印出来的是Unicode编码,这个是无解的. 对 ...

  • python笔记6-%u60A0和\u60a0类似unicode解码

    前言 有时候从接口的返回值里面获取到的是类似"%u4E0A%u6D77%u60A0%u60A0"这种格式的编码,不是python里面的unicode编码. python里面的uni ...