Python 上下文(Context)学习笔记
前言
最早接触到with语句的时候,是初学python,对文件进行读写的时候,当时文件读写一般都是用open()函数来对文件进行读写,为了防止读写的过程中出现错误,也为了让代码更加的pythonic,会接触到with语句
with open('file.text', 'w') as f:
f.write('hello')
上面的代码仅需两行就实现了对文件进行写入的操作,很方便,代码也更整洁,不会出错。
事实上,上面一段代码就用到了上下文管理器的知识。
某种程度上,上下文管理器可以理解成try/finally的优化,使得代码更加易读,在通常情况下,我们读取文件的时候,如果不适用with语句,为了防止出错,可以采用try/finally的语句来进行读取,使得文件可以正常执行close()方法。
f = open('file.text', 'w'):
try:
f.write('hello')
finally:
f.close()
很明显,with语句比try/finally更易读,更友好。
上下文管理器
上下文管理器协议,是指要实现对象的 __enter__() 和 __exit__() 方法。
上下文管理器也就是支持上下文管理器协议的对象,也就是实现了 __enter__() 和 __exit__() 方法。
上下文管理器 是一个对象,它定义了在执行 with 语句时要建立的运行时上下文。 上下文管理器处理进入和退出所需运行时上下文以执行代码块。 通常使用 with 语句来使用,但是也可以通过直接调用它们的方法来使用。
简单来说,我们定义一个上下文管理器,需要在一个类里面一个实现__enter__(self) 和 __exit__(self, exc_type, exc_value, traceback) 方法。
object.__enter__(self)进入与此对象相关的运行时上下文,并返回自身或者另一个与运行食上下文相关的对象。(with语句将会绑定这个方法的返回值到
as子句中指定的目标)object.__exit__(self, exc_type, exc_value, traceback)退出关联到此对象的运行时上下文。 各个参数描述了导致上下文退出的异常。 如果上下文是无异常地退出的,三个参数都将为None。如果提供了异常,并且希望方法屏蔽此异常(即避免其被传播),则应当返回真值。 否则的话,异常将在退出此方法时按正常流程处理。请注意
__exit__()方法不应该重新引发被传入的异常,这是调用者的责任。如果 with_body 的退出由异常引发,并且__exit__()的返回值等于 False,那么这个异常将被重新引发一次;如果__exit__()的返回值等于 True,那么这个异常就被无视掉,继续执行后面的代码。
通常情况下,我们会使用with语句来使用上下文管理器:
with context_expr [as var]:
with_body
执行过程:
- 执行上下文表达式(context_expr)以获得上下文管理器对象。
- 加载上下文管理器对象的
__exit__()方法,备用。 - 执行上下文管理器对象的
__enter__()方法。 - 如果有
as var语句,将__enter__()方法返回值绑定到 as 后面的 变量中。 - 执行 with 内的代码块(with_body)。
- 执行上下文管理器的__exit__()方法。
把文章开头的例子用上下文管理器实现一边:
class OpenFile(object):
def __init__(self, filename):
self.file = open(filename, 'w+')
def __enter__(self):
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
def main():
with OpenFile('text.txt') as f:
f.write('ok')
if __name__ == "__main__":
main()
总结:在上下文管理器中,生成类实例的时候,会自动调用__enter__()方法,而在结束的时候,会自动调用__exit__()方法。
所以,在定义上下文管理器的时候,我们只需实现好这两个方法就行了。
上下文管理器的运用场景
上下文管理器的典型用法包括保存和恢复各种全局状态,锁定和解锁资源,关闭打开的文件等。
比如我们需要在一段代码中使用到数据库的查询,可以通过上下文处理器来优化我们的代码结构,
contextilb模块
contextilb模块是python内置模块中的一个用于上下文的模块,可以让我们更优雅地使用上下文管理器。
@contextmanager
这是contextlib模块提供的一个装饰器,用于将一个函数声明上下文管理,无需创建一个类或者单独的__enter__()方法和__exit__()方法,就可以实现上下文管理。
需要注意的是,被装饰的函数被调用的时候必须返回一个生成器,而且这个生成器只生成一个值,如果有as的话,该值讲绑定到with语句as子句的目标中。
from contextlib import contextmanager
@contextmanager
def tag(name):
print('<{}>'.format(name))
yield
print('</{}>'.format(name))
with tag('title'):
print("This is a contextmanger test")
输出为:
<title>
This is a contextmanger test
</title>
可以看出,输出的流程:
- 先输出
yield前的输出语句; - 然后再是
tag()函数的输出语句, - 最后是
yield后面的输出语句。
在生成器函数中的yield之前的语句在__enter__()方法中执行,
相当于
def __enter__(self):
print('<{}>'.format(name))
def __exit__(self, exc_type, exc_val, exc_tb):
print('</{}>'.format(name))
closing
返回一个上下文管理器,在完成代码块的时候会关闭参数
源码参考:
class closing(AbstractContextManager):
def __init__(self, thing):
self.thing = thing
def __enter__(self):
return self.thing
def __exit__(self, *exc_info):
self.thing.close()
常见用法,如写爬虫的时候,可以这样写:
from contextlib import closing
import requests
url = 'http://www.baidu.com'
with closing(requests.get(url)) as page:
for line in page:
print(page)
上下文管理器查询数据库
代码:
import pymysql
class Database(object):
def __init__(self):
self.db = pymysql.connect("localhost", "root", "root", "test")
self.cursor = self.db.cursor()
def query(self, sql):
self.cursor.execute(sql)
result = self.cursor.fetchone()
return result
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
self.db.close()
def main():
sql = "SELECT password FROM USER WHERE username='{}' ORDER BY 1;".format('admin')
with Database() as s:
a = s.query(sql)
print(a)
if __name__ == "__main__":
main()
使用contextlib模块编写:
class Database(object):
def __init__(self):
self.db = pymysql.connect("localhost", "root", "root", "test")
self.cursor = self.db.cursor()
def query(self, sql):
self.cursor.execute(sql)
result = self.cursor.fetchone()
return result
@contextmanager
def database_query():
q = Database()
yield q
def main():
sql = "SELECT password FROM USER WHERE username='{}' ORDER BY 1;".format('admin')
with database_query() as s:
a = s.query(sql)
print(a)
if __name__ == "__main__":
main()
Python 上下文(Context)学习笔记的更多相关文章
- Requests:Python HTTP Module学习笔记(一)(转)
Requests:Python HTTP Module学习笔记(一) 在学习用python写爬虫的时候用到了Requests这个Http网络库,这个库简单好用并且功能强大,完全可以代替python的标 ...
- python网络爬虫学习笔记
python网络爬虫学习笔记 By 钟桓 9月 4 2014 更新日期:9月 4 2014 文章文件夹 1. 介绍: 2. 从简单语句中開始: 3. 传送数据给server 4. HTTP头-描写叙述 ...
- Python Built-in Function 学习笔记
Python Built-in Function 学习笔记 1. 匿名函数 1.1 什么是匿名函数 python允许使用lambda来创建一个匿名函数,匿名是因为他不需要以标准的方式来声明,比如def ...
- Python快速入门学习笔记(二)
注:本学习笔记参考了廖雪峰老师的Python学习教程,教程地址为:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb49318210 ...
- python数据分析入门学习笔记
学习利用python进行数据分析的笔记&下星期二内部交流会要讲的内容,一并分享给大家.博主粗心大意,有什么不对的地方欢迎指正~还有许多尚待完善的地方,待我一边学习一边完善~ 前言:各种和数据分 ...
- python网络爬虫学习笔记(二)BeautifulSoup库
Beautiful Soup库也称为beautiful4库.bs4库,它可用于解析HTML/XML,并将所有文件.字符串转换为'utf-8'编码.HTML/XML文档是与“标签树一一对应的.具体地说, ...
- Python之xml学习笔记
XML处理模块 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,至今很多传统公司如金融行业的很多系统的接口还主要是xml. xml的格式如下,就是通过&l ...
- python网络爬虫学习笔记(一)Request库
一.Requests库的基本说明 引入Rquests库的代码如下 import requests 库中支持REQUEST, GET, HEAD, POST, PUT, PATCH, DELETE共7个 ...
- Python基础教程学习笔记:第一章 基础知识
Python基础教程 第二版 学习笔记 1.python的每一个语句的后面可以添加分号也可以不添加分号:在一行有多条语句的时候,必须使用分号加以区分 2.查看Python版本号,在Dos窗口中输入“p ...
- python数据分析入门学习笔记儿
学习利用python进行数据分析的笔记儿&下星期二内部交流会要讲的内容,一并分享给大家.博主粗心大意,有什么不对的地方欢迎指正~还有许多尚待完善的地方,待我一边学习一边完善~ 前言:各种和数据 ...
随机推荐
- JavaScript学习——使用JS实现首页轮播图效果
1.相关技术 获取元素 document.getElementById(“id 名称”) 事件(onload) 只能写一次并且放到body标签中 定时操作:setInterval(“changeImg ...
- oracle动态磁盘管理
一.ASM发展: oracle 想做硬件: 收购sum公司的小型机业务 推出一体机Exadata 1.sum服务器 2.磁盘柜(没做存储) 推出asm建库方式配合一体机(自动使用磁盘柜的盘)(抛弃ra ...
- SpringCloud学习笔记(8)----Spring Cloud Netflix之负载均衡-Ribbon的负载均衡的策略
一. 内置 负载均衡策略的介绍的 IRule的实现类 2. 通过代码实现负载均衡 在第六节Riddom的使用的工程中,随机策略配置类 package com.wangx.cloud.springclo ...
- Vue 中 换行符获取
当要获取到 vue 中 文本域的换行符时, 需要用到正则匹配. let reg = new RegExp('/n',"g"); let str = text.replace(reg ...
- bzoj4551 [HEOI2016]树
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
- qume-kvm 命令管理
sudo /etc/init.d/acpid start 安装管理包工具 sudo apt install libguestfs-tools [ qemu-kvm qemu-kvm-tools vir ...
- Python: Json串反序列化为自定义类对象
最近刚接触到python,就想到了如何反序列化json串.网上找了一下,大部分都是用json模块反序列化为python数据结构(字典和列表).如果对json模块不了解的参考菜鸟教程.然后我在此基础上将 ...
- 什么是面向对象以及其意义,prototpye原型
什么是面向对象: 使用对象时,只关注对象提供的功能,不关注其内部的细节 例如:jquery 什么是对象: 对象是一个整体对外提供一些操作,比如 收音机 面向对象编程OOP的特点: 1.抽象:把主要的特 ...
- 最强最全干货分享:Android开发书籍、教程、工具等
最全干货分享,本文收集整理了Android开发所需的书籍.教程.工具.资讯和周刊各种资源,它们能让你在Android开发之旅的各个阶段都受益. 入门<Learning Android(中文版)& ...
- Invalid property 'sentinels' of bean class redis spring 错误修改
/* * Copyright 2014-2015 the original author or authors. * * Licensed under the Apache License, Vers ...