python 的 with 语法的深入理解
如果你有一些对象 (比如一个文件、网络连接或锁),需要支持 with 语句,下面介绍两种定义方法。
方法(1):
首先介绍下 with 工作原理
(1)紧跟 with 后面的语句被求值后,返回对象的“enter()”方法被调用,这个方法的返回值将被赋值给 as 后面的变量;
(2)当 with 后面的代码块全部被执行完之后,将调用前面返回对象的“exit()”方法。
with 工作原理代码示例:
|
1
2
3
4
5
6
7
8
9
10
|
class
Sample:
def
__enter__(``self``):
print
"in __enter__"
return
"Foo"
def
__exit__(``self``, exc_type, exc_val, exc_tb):
print
"in __exit__"
def
get_sample():
return
Sample()
with get_sample() as sample:
print
"Sample: "``, sample
|
代码的运行结果如下:
|
1
2
3
|
in
__enter__
Sample: Foo
in
__exit__
|
可以看到,整个运行过程如下:
(1)enter() 方法被执行;
(2)enter() 方法的返回值,在这个例子中是”Foo”,赋值给变量 sample;
(3)执行代码块,打印 sample 变量的值为”Foo”;
(4)exit() 方法被调用;
【注:】exit() 方法中有3个参数, exc_type, exc_val, exc_tb,这些参数在异常处理中相当有用。
exc_type: 错误的类型
exc_val: 错误类型对应的值
exc_tb: 代码中错误发生的位置
示例代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
Sample():
def
__enter__(``self``):
print``(``'in enter'``)
return
self
def
__exit__(``self``, exc_type, exc_val, exc_tb):
print
"type: "``, exc_type
print
"val: "``, exc_val
print
"tb: "``, exc_tb
def
do_something(``self``):
bar ``=
1
/
0
return
bar ``+
10
with Sample() as sample:
sample.do_something()
|
程序输出结果:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
in
enter
Traceback (most recent call last):
type``: <``type
'exceptions.ZeroDivisionError'``>
val: integer division ``or
modulo by zero
File
"/home/user/cltdevelop/Code/TF_Practice_2017_06_06/with_test.py"``, line ``36``, ``in
``
tb: ``object
at ``0x7f9e13fc6050``>
sample.do_something()
File
"/home/user/cltdevelop/Code/TF_Practice_2017_06_06/with_test.py"``, line ``32``, ``in
do_something
bar ``=
1
/
0
ZeroDivisionError: integer division ``or
modulo by zero
Process finished with exit code ``1
|
方法(2):
实现一个新的上下文管理器的最简单的方法就是使用 contexlib 模块中的 @contextmanager 装饰器。 下面是一个实现了代码块计时功能的上下文管理器例子:
|
1
2
3
4
5
6
7
8
9
10
11
|
import
time
from
contextlib ``import
contextmanager
@contextmanager
def
timethis(label):
start ``=
time.time()
try``:
yield
finally``:
end ``=
time.time()
print``(``'{}: {}'``.``format``(label, end ``-
start))
|
|
1
2
3
4
5
|
# Example use
with timethis(``'counting'``):
n ``=
10000000
while
n > ``0``:
n ``-``=
1
|
在函数 timethis() 中,yield 之前的代码会在上下文管理器中作为 enter() 方法执行, 所有在 yield 之后的代码会作为 exit() 方法执行。 如果出现了异常,异常会在 yield 语句那里抛出。
【注】
1、@contextmanager 应该仅仅用来写自包含的上下文管理函数。
2、如果你有一些对象 (比如一个文件、网络连接或锁),需要支持 with 语句,那么你就需要单独实现 enter() 方法和 exit() 方法。
下次我注意
谢谢分享,前面这个行号有些难受