with语句是从Python 2.5开始引入的一种与异常处理相关的功能(2.5版本中要通过 from __future__ import with_statement导入后才可以使用, 从2.6 版本开始缺省可用。 with/as语句的设计是用于作为常见的try/finally用法模式的替代方案,确保执行一些必要的终止或者清理工作。python以环境管理器强化一些内置工具, 例如自动关闭文件,线程中的自动上锁和解锁等,程序员也可以用类来编写自己的环境管理器(__enter__, __exit__)。
expression要返回一个对象,从而支持环境管理协议(下面会谈到这个协议)。 as语句是选用的,expression语句会返回一个值,这个值会赋值给as后面的variable变量。(注意不是把这个对象赋值给variable变量,是把这个对象返回 一个值赋值给variable,这个后面会谈到) python的一些内置对象已经支持了环境管理协议,如文件对象。使用with语句可以在with_block之后自动关闭文件,无论是否发生异常。 for example:
with open("test_file.txt", "r") as myfile: for line in myfile: print(line)这里打开文件对象后,返回了被打开文件对象的实例并赋给了myfile变量。对myfile处理完成后,with语句会自动执行myfile.close()。 相当于try/finally语句:
myfile = open("test_file.txt", "r") try: for line in myfile: print(line) finally: myfile.close()python有一些内置对象已经带有了环境管理器,如刚刚谈过的文件对象。但我们仍然可以自己编写一个带有环境管理器的类。 这个实际上就是类的运算符重载,需要使用__enter__和__exit__。 先简述一些with语句的实际工作流程: 1. 计算expression表达式,所得到的对象就称为环境管理器,它必须有__enter__和__exit__方法。 2. 环境管理器的__enter__会首先被调用,如果存在as语句,它的返回值会赋给as后面的变量,否则直接丢弃。 3. with_block中的代码块会执行。 4. 最后执行__exit__(type, value, traceback)方法。 - 如果with语句引发了异常,且该方法的返回值是假,那么异常就会被重新引发,否则异常会终止。正常情况下我们希望异常被重新引发,这样才能讲异常 传递到with代码块外面去,便于发现和处理。 - 如果with语句没有引发异常,其中type, value, traceback参数都会以None值传递。
现在我们来自己定义一个环境管理器对象:
class Person: def __init__(self, name): self.name = name def call(self, number): print("{person} is calling {number}".format(person=self.name, number=number)) def __enter__(self): print("{person} pick up the phone".format(person=self.name)) print("ready to call") return self def __exit__(self, exit_type, exit_value, exit_traceback): try: if exit_type is None: print("ok, thers is no exception. ") else: print("there are some exceptions") print(exit_type, exit_value, exit_traceback) return False finally: print("{person} cut the call".format(person=self.name)) # return True # test1 with Person("Jon") as jon: jon.call("110") # test 2 with Person("Jon") as jon: jon.call("120") raise ValueError("the phone number is wrong")输出:
# test1 Jon pick up the phone ready to call Jon is calling 110 ok, thers is no exception. Jon cut the call # test 2 Jon pick up the phone ready to call Jon is calling 120 there are some exceptions (<type 'exceptions.ValueError'>, ValueError('the phone number is wrong',), <traceback object at 0x7fa717085a28>) Jon cut the call Traceback (most recent call last): File "/home/vincent/github/practice/test.py", line 35, in <module> raise ValueError("the phone number is wrong") ValueError: the phone number is wrong关于异常是否抛出: 前面已经说过,在__exit__中如果返回假(包括False,None)等,那么with语句中出现的异常就会被抛出来。如上面test2的输出一样。 但如果return True,如我上面注掉的一样,那么with语句中出现的异常就会被自动过滤掉,不会抛出来,那么test2的输出就该是:
Jon pick up the phone ready to call Jon is calling 120 there are some exceptions (<type 'exceptions.ValueError'>, ValueError('the phone number is wrong',), <traceback object at 0x7fa717085a28>) Jon cut the call发现没有,虽然with语句捕获到了异常,我们在代码里手动把它print了出来。但实际上并不会抛出异常,也就是说代码会忽略这个异常继续执行下去。 建议将异常抛出,除非你有十足的理由。
