python with-as statement


Guide

file example

读取文件处理传统写法,需要确保文件正常打开之后,即使出现异常,也能够正常关闭。写法不够优雅。

try:
    file = open("./12.txt")
except:
    print("can not open file")
    exit(1)

try:
    data = file.read()
    # ... do somethings
except:
    print("exception")
finally:
    print("OK. close file")
    file.close()

with语句时用于对try except finally 的优化,让代码更加美观。同时确保即使执行异常,finally中的语句也可以执行。

with-as 重写

with open("./1.txt") as file:
    data = file.read()

with-as 原理

基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。

原理:

  1. with后面的语句被求值后返回对象A;
  2. 调用对象A的__enter__()方法将返回值赋给as后面的变量B;
  3. 执行with后面的代码块;
  4. 当with后面的代码块全部被执行完之后(或者由于抛出异常没有正常习执行完毕),最终都会调用A对象的__exit__()方法进行清理工作。
class Sample:
    def __enter__(self):
        print "In __enter__()"
        return "Foo"

    def __exit__(self, type, value, trace):
        print "In __exit__()"

def get_sample():
    return Sample()

with get_sample() as s:
    print "sample:", s

output

In __enter__()
sample: Foo
In __exit__()

steps:

  1. 执行with后面的语句get_sample()返回对象sample
  2. 执行sample对象的__enter__()方法,返回string类型的”Foo”赋值给as后面的s变量
  3. 执行代码块,打印变量”sample”的值为 “Foo”
  4. 代码块执行完毕,调用sample对象的__exit__()方法进行清理工作

with 处理异常

with除了可以进行清理工作之外,真正强大之处是可以处理异常。Sample类的__exit__方法有三个参数:val, typetrace。 这些参数在异常处理中相当有用。

class Sample:
    def __enter__(self):
        return self

    def __exit__(self, type, value, trace):
        print "type:", type
        print "value:", value
        print "trace:", trace

    def do_something(self):
        bar = 1/0
        return bar + 10

with Sample() as sample:
    sample.do_something()

output

type: <type 'exceptions.ZeroDivisionError'>
value: integer division or modulo by zero
trace: <traceback object at 0x0000000004A5AA08>

ZeroDivisionErrorTraceback (most recent call last)
<ipython-input-9-282d3906c5ac> in <module>()
     13 
     14 with Sample() as sample:
---> 15     sample.do_something()

<ipython-input-9-282d3906c5ac> in do_something(self)
      9 
     10     def do_something(self):
---> 11         bar = 1/0
     12         return bar + 10
     13 

ZeroDivisionError: integer division or modulo by zero

在执行代码块sample.do_something()的时候由于异常抛出,与之关联的type,value和stack trace传给__exit__()方法,因此抛出的ZeroDivisionError异常被打印出来了。

file 对象

python中的file对象已经实现了__enter__()__exit__()方法

file = open("./1.txt")
file.__enter__()
<open file './1.txt', mode 'r' at 0x00000000049FF030>
file.read(1)
file.__exit__()
file.read(1)
ValueErrorTraceback (most recent call last)

<ipython-input-19-d0e1662399bb> in <module>()
----> 1 file.read(1)


ValueError: I/O operation on closed file

Reference

History

  • 20181023: created.

Author: kezunlin
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source kezunlin !
评论
  TOC