- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Python标准模块--ContextManager上下文管理器的具体用法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
写代码时,我们希望把一些操作放到一个代码块中,这样在代码块中执行时就可以保持在某种运行状态,而当离开该代码块时就执行另一个操作,结束当前状态;所以,简单来说,上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取“处理”.
这一功能是在Python2.5之后引进的,它的优势在于可以使得你的代码更具可读性,且不容易出错.
1 模块简介 。
在数年前,Python 2.5 加入了一个非常特殊的关键字,就是with。with语句允许开发者创建上下文管理器。什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情。例如,你可能想要打开一个文件,然后写入一些内容,最后再关闭文件。这或许就是上下文管理器中一个最经典的示例。事实上,当你利用with语句打开一个文件时,Python替你自动创建了一个上下文管理器.
1
2
|
with
open
(
"test/test.txt"
,
"w"
) as f_obj:
f_obj.write(
"hello"
)
|
如果你使用的是Python 2.4,你不得不以一种老的方式来完成这个任务.
1
2
3
|
f_obj
=
open
(
"test/test.txt"
,
"w"
)
f_obj.write(
"hello"
)
f_obj.close()
|
上下文管理器背后工作的机制是使用Python的方法:__enter__和__exit__。让我们尝试着去创建我们的上下文管理器,以此来了解上下文管理器是如何工作的.
2 模块使用 。
2.1 创建一个上下文管理器类 。
与其继续使用Python打开文件这个例子,不如我们创建一个上下文管理器,这个上下文管理器将会创建一个SQLite数据库连接,当任务处理完毕,将会将其关闭。下面就是一个简单的示例.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import
sqlite3
class
DataConn:
def
__init__(
self
,db_name):
self
.db_name
=
db_name
def
__enter__(
self
):
self
.conn
=
sqlite3.connect(
self
.db_name)
return
self
.conn
def
__exit__(
self
,exc_type,exc_val,exc_tb):
self
.conn.close()
if
exc_val:
raise
if
__name__
=
=
"__main__"
:
db
=
"test/test.db"
with DataConn(db) as conn:
cursor
=
conn.cursor()
|
在上述代码中,我们创建了一个类,获取到SQLite数据库文件的路径。__enter__方法将会自动执行,并返回数据库连接对象。现在我们已经获取到数据库连接对象,然后我们创建光标,向数据库写入数据或者对数据库进行查询。当我们退出with语句的时候,它将会调用__exit__方法用于执行和关闭这个连接.
让我们使用其它的方法来创建上下文管理器.
2.2 利用contextlib创建一个上下文管理器 。
Python 2.5 不仅仅添加了with语句,它也添加了contextlib模块。这就允许我们使用contextlib的contextmanager函数作为装饰器,来创建一个上下文管理器。让我们尝试着用它来创建一个上下文管理器,用于打开和关闭文件.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
from
contextlib
import
contextmanager
@contextmanager
def
file_open(path):
try
:
f_obj
=
open
(path,
"w"
)
yield
f_obj
except
OSError:
print
(
"We had an error!"
)
finally
:
print
(
"Closing file"
)
f_obj.close()
if
__name__
=
=
"__main__"
:
with file_open(
"test/test.txt"
) as fobj:
fobj.write(
"Testing context managers"
)
|
在这里,我们从contextlib模块中引入contextmanager,然后装饰我们所定义的file_open函数。这就允许我们使用Python的with语句来调用file_open函数。在函数中,我们打开文件,然后通过yield,将其传递出去,最终主调函数可以使用它.
一旦with语句结束,控制就会返回给file_open函数,它继续执行yield语句后面的代码。这个最终会执行finally语句--关闭文件。如果我们在打开文件时遇到了OSError错误,它就会被捕获,最终finally语句依然会关闭文件句柄.
contextlib.closing(thing) 。
contextlib模块提供了一些很方便的工具。第一个工具就是closing类,一旦代码块运行完毕,它就会将事件关闭。Python官方文档给出了类似于以下的一个示例, 。
1
2
3
4
5
6
7
|
>>>
from
contextlib
import
contextmanager
>>> @contextmanager
...
def
closing(db):
...
try
:
...
yield
db.conn()
...
finally
:
... db.close()
|
在这段代码中,我们创建了一个关闭函数,它被包裹在contextmanager中。这个与closing类相同。区别就是,我们可以在with语句中使用closing类本身,而非装饰器。让我们看如下的示例, 。
1
2
3
4
5
|
>>>
from
contextlib
import
closing
>>>
from
urllib.request
import
urlopen
>>> with closing(urlopen(
"http://www.google.com"
)) as webpage:
...
for
line
in
webpage:
...
pass
|
在这个示例中,我们在closing类中打开一个url网页。一旦我们运行完毕with语句,指向网页的句柄就会关闭.
contextlib.suppress(*exceptions) 。
另一个工具就是在Python 3.4中加入的suppress类。这个上下文管理工具背后的理念就是它可以禁止任意数目的异常。假如我们想忽略FileNotFoundError异常。如果你书写了如下的上下文管理器,那么它不会正常运行.
1
2
3
4
5
6
7
|
>>> with
open
(
"1.txt"
) as fobj:
...
for
line
in
fobj:
...
print
(line)
...
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
FileNotFoundError: [Errno
2
] No such
file
or
directory:
'1.txt'
|
正如你所看到的,这个上下文管理器没有处理这个异常,如果你想忽略这个错误,你可以按照如下方式来做, 。
1
2
3
4
5
|
>>>
from
contextlib
import
suppress
>>> with suppress(FileNotFoundError):
... with
open
(
"1.txt"
) as fobj:
...
for
line
in
fobj:
...
print
(line)
|
在这段代码中,我们引入suppress,然后将我们要忽略的异常传递给它,在这个例子中,就是FileNotFoundError。如果你想运行这段代码,你将会注意到,文件不存在时,什么事情都没有发生,也没有错误被抛出。请注意,这个上下文管理器是可重用的,2.4章节将会具体解释.
contextlib.redirect_stdout/redirect_stderr 。
contextlib模块还有一对用于重定向标准输出和标准错误输出的工具,分别在Python 3.4 和3.5 中加入。在这些工具被加入之前,如果你想对标准输出重定向,你需要按照如下方式操作, 。
1
2
3
4
5
6
|
import
sys
path
=
"test/test.txt"
with
open
(path,
"w"
) as fobj:
sys.stdout
=
fobj
help
(
sum
)
|
利用contextlib模块,你可以按照如下方式操作, 。
1
2
3
4
5
6
7
|
from
contextlib
import
redirect_stdout
path
=
"test/test.txt"
with
open
(path,
"w"
) as fobj:
with redirect_stdout(fobj):
help
(redirect_stdout)
|
在上面两个例子中,我们均是将标准输出重定向到一个文件。当我们调用Python的help函数,不是将信息输出到标准输出上,而是将信息保存到重定向的文件中。你也可以将标准输出重定向到缓存或者从用接口如Tkinter或wxPython中获取的文件控制类型上.
2.3 ExitStack 。
ExitStack是一个上下文管理器,允许你很容易地与其它上下文管理结合或者清除。这个咋听起来让人有些迷糊,我们来看一个Python官方文档的例子,或许会让我们更容易理解它.
1
2
3
4
|
>>>
from
contextlib
import
ExitStack
>>> filenames
=
[
"1.txt"
,
"2.txt"
]
>>> with ExitStack as stack:
... file_objects
=
[stack.enter_context(
open
(filename))
for
filename
in
filenames]
|
这段代码就是在列表中创建一系列的上下文管理器。ExitStack维护一个寄存器的栈。当我们退出with语句时,文件就会关闭,栈就会按照相反的顺序调用这些上下文管理器.
Python官方文档中关于contextlib有很多示例,你可以学习到如下的技术点:
2.4 可重用的上下文管理器 。
大部分你所创建的上下文管理器仅仅只能在with语句中使用一次,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
>>>
from
contextlib
import
contextmanager
>>> @contextmanager
...
def
single():
...
print
(
"Yielding"
)
...
yield
...
print
(
"Exiting context manager"
)
...
>>> context
=
single()
>>> with context:
...
pass
...
Yielding
Exiting context manager
>>> with context:
...
pass
...
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
File
"/usr/lib/python3.4/contextlib.py"
, line
61
,
in
__enter__
raise
RuntimeError(
"generator didn't yield"
)
from
None
RuntimeError: generator didn't
yield
|
在这段代码中,我们创建了一个上下文管理器实例,并尝试着在Python的with语句中运行两次。当第二次运行时,它抛出了RuntimeError.
但是如果我们想运行上下文管理器两次呢?我们需要使用可重用的上下文管理器。让我们使用之前所用过的redirect_stdout这个上下文管理器作为示例, 。
1
2
3
4
5
6
7
8
9
10
11
12
|
>>>
from
contextlib
import
redirect_stdout
>>>
from
io
import
StringIO
>>> stream
=
StringIO()
>>> write_to_stream
=
redirect_stdout(stream)
>>> with write_to_stream:
...
print
(
"Write something to the stream"
)
... with write_to_stream:
...
print
(
"Write something else to stream"
)
...
>>>
print
(stream.getvalue())
Write something to the stream
Write something
else
to stream
|
在这段代码中,我们创建了一个上下文管理器,它们均向StringIO(一种内存中的文件流)写入数据。这段代码正常运行,而没有像之前那样抛出RuntimeError错误,原因就是redirect_stdout是可重用的,允许我们可以调用两次。当然,实际的例子将会有更多的函数调用,会更加的复杂。一定要注意,可重用的上下文管理器不一定是线程安全的。如果你需要在线程中使用它,请先仔细阅读Python的文档.
2.5 总结 。
上下文管理器很有趣,也很方便。我经常在自动测试中使用它们,例如,打开和关闭对话。现在,你应该可以使用Python内置的工具去创建你的上下文管理器。你还可以继续阅读Python关于contextlib的文档,那里有很多本文没有覆盖到的知识.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:http://www.cnblogs.com/zhbzz2007/p/6158125.html 。
最后此篇关于Python标准模块--ContextManager上下文管理器的具体用法的文章就讲到这里了,如果你想了解更多关于Python标准模块--ContextManager上下文管理器的具体用法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我最近在我的机器上安装了 cx_Oracle 模块,以便连接到远程 Oracle 数据库服务器。 (我身边没有 Oracle 客户端)。 Python:版本 2.7 x86 Oracle:版本 11.
我想从 python timeit 模块检查打印以下内容需要多少时间,如何打印, import timeit x = [x for x in range(10000)] timeit.timeit("
我盯着 vs 代码编辑器上的 java 脚本编码,当我尝试将外部模块包含到我的项目中时,代码编辑器提出了这样的建议 -->(文件是 CommonJS 模块;它可能会转换为 ES6 模块。 )..有什么
我有一个 Node 应用程序,我想在标准 ES6 模块格式中使用(即 "type": "module" in the package.json ,并始终使用 import 和 export)而不转译为
我正在学习将 BlueprintJS 合并到我的 React 网络应用程序中,并且在加载某些 CSS 模块时遇到了很多麻烦。 我已经安装了 npm install @blueprintjs/core和
我需要重构一堆具有这样的调用的文件 define(['module1','module2','module3' etc...], function(a, b, c etc...) { //bun
我是 Angular 的新手,正在学习各种教程(Codecademy、thinkster.io 等),并且已经看到了声明应用程序容器的两种方法。首先: var app = angular.module
我正在尝试将 OUnit 与 OCaml 一起使用。 单元代码源码(unit.ml)如下: open OUnit let empty_list = [] let list_a = [1;2;3] le
我在 Angular 1.x 应用程序中使用 webpack 和 ES6 模块。在我设置的 webpack.config 中: resolve: { alias: { 'angular':
internal/modules/cjs/loader.js:750 return process.dlopen(module, path.toNamespacedPath(filename));
在本教程中,您将借助示例了解 JavaScript 中的模块。 随着我们的程序变得越来越大,它可能包含许多行代码。您可以使用模块根据功能将代码分隔在单独的文件中,而不是将所有内容都放在一个文件
我想知道是否可以将此代码更改为仅调用 MyModule.RED 而不是 MyModule.COLORS.RED。我尝试将 mod 设置为变量来存储颜色,但似乎不起作用。难道是我方法不对? (funct
我有以下代码。它是一个 JavaScript 模块。 (function() { // Object var Cahootsy; Cahootsy = { hello:
关闭。这个问题是 opinion-based 。它目前不接受答案。 想要改进这个问题?更新问题,以便 editing this post 可以用事实和引文来回答它。 关闭 2 年前。 Improve
从用户的角度来看,一个模块能够通过 require 加载并返回一个 table,模块导出的接口都被定义在此 table 中(此 table 被作为一个 namespace)。所有的标准库都是模块。标
Ruby的模块非常类似类,除了: 模块不可以有实体 模块不可以有子类 模块由module...end定义. 实际上...模块的'模块类'是'类的类'这个类的父类.搞懂了吗?不懂?让我们继续看
我有一个脚本,它从 CLI 获取 3 个输入变量并将其分别插入到 3 个变量: GetOptions("old_path=s" => \$old_path, "var=s" =
我有一个简单的 python 包,其目录结构如下: wibble | |-----foo | |----ping.py | |-----bar | |----pong.py 简单的
这种语法会非常有用——这不起作用有什么原因吗?谢谢! module Foo = { let bar: string = "bar" }; let bar = Foo.bar; /* works *
我想运行一个命令: - name: install pip shell: "python {"changed": true, "cmd": "python <(curl https://boot
我是一名优秀的程序员,十分优秀!