对象,是 Python 对数据的抽象概念。Python 中的所有数据都是以对象或对象间的关系来实现的。(某种意义上,代码也是由对象来实现的,这与冯·诺依曼的“stored program computer”模型相一致)。每个对象都有 id、type、和 value。对象的 id 从创建之时起就不再改变,你可以把它想象成对象在内存中的地址。is 运算符就是比较的两个对象的 id,或者你也可以通过内建的 id() 函数来直接查看。
正文:
with 语句
with 语句是被设计用来简化“try / finally”语句的。通常的用处在于共享资源的获取和释放,比如文件、数据库和线程资源。它的用法如下:
with context_exp [as var]:
with_suit
with 语句也是复合语句的一种,就像 if、try 一样,它的后面也有个“:”,并且紧跟一个缩进的代码块 with_suit。context_exp 表达式的作用是提供一个上下文管理器(Context Manager),整个 with_suit 代码块都是在这个上下文管理器的运行环境下执行的。context_exp 可以直接是一个上下文管理器的引用,也可以是一句可执行的表达式,with 语句会自动执行这个表达式以获得上下文管理对象。with 语句的实际执行流程是这样的:
- 执行 context_exp 以获取上下文管理器
- 加载上下文管理器的 __exit__() 方法以备稍后调用
- 调用上下文管理器的 __enter__() 方法
- 如果有 as var 从句,则将 __enter__() 方法的返回值赋给 var
- 执行子代码块 with_suit
- 调用上下文管理器的 __exit__() 方法,如果 with_suit 的退出是由异常引发的,那么该异常的 type、value 和 traceback 会作为参数传给 __exit__(),否则传三个 None
- 如果 with_suit 的退出由异常引发,并且 __exit__() 的返回值等于 False,那么这个异常将被重新引发一次;如果 __exit__() 的返回值等于 True,那么这个异常就被无视掉,继续执行后面的代码
即,可以把 __exit__() 方法看成是“try / finally”的 finally,它总是会被自动调用。Python 里已经有了一些支持上下文管理协议的对象,比如文件对象,在使用 with 语句处理文件对象时,可以不再关心“打开的文件必须记得要关闭”这个问题了:
>>> with open('test.py') as f:
print(f.readline())
#!/usr/bin/env python
>>> f.readline()
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
f.readline()
ValueError: I/O operation on closed file.
可以看到在 with 语句完成后,f 已经自动关闭了,这个过程就是在 f 的__exit__() 方法里完成的。然后下面再来详细介绍一下上下文管理器:
内建类型:上下文管理器类型(Context Manager Types)
Python 使用上下文管理协议定义了一种运行时上下文环境(runtime context)。上下文管理协议由包含了一对方法的上下文管理对象实现:它会在子代码块运行前进入一个运行时上下文,并在代码块结束后退出该上下文。我们一般使用 with 语句来调用上下文管理对象,这样代码清晰度较好。
上下文管理器的两个方法:
contextmanager.__enter__()
本方法进入运行时上下文环境,并返回自身或另一个与运行时上下文相关的对象。返回值会赋给 as 从句后面的变量,as 从句是可选的
举一个返回自身的栗子比如文件对象。因为文件对象自身就是一个上下文管理器 ,所以“open()”语句返回的文件对象既被 with 获取,也会经由本方法传给 as。由下例可见,文件对象内就包含了上下文管理器的两个方法:
with open('test.py') as f: 'enter' in dir(f) 'exit' in dir(f)
True True
返回一个相关对象的例子比如 decimal.localcontext() 所返回的对象。这个对象的 __enter__() 将 decimal 的主动上下文(active context)放到了一份原版 decimal 上下文的拷贝中并返回。这使得用户在改动 with 语句内的上下文环境时不会影响到语句外的部分:
import decimal with decimal.localcontext() as ctx: ctx.prec = 22 print(decimal.getcontext().prec)
22
print(decimal.getcontext().prec) 28
有一种 with 的用法是直接使用“with f=open('test.py'):”这样的语句,不用 as。这种做法在上面提到的 __enter__() 返回 self 时还好说,但如果返回的是另一个对象比如第二个例子的时候,就不好了。所以这里的话还是推荐一律使用 as 来赋值。
contextmanager.__exit__(exc_type,exc_val,exc_tb)
本方法退出当前运行时上下文并返回一个布尔值,该布尔值标明了“如果 with_suit 的退出是由异常引发的,该异常是否须要被忽略”。如果 __exit__() 的返回值等于 False,那么这个异常将被重新引发一次;如果 __exit__() 的返回值等于 True,那么这个异常就被无视掉,继续执行后面的代码
另外当 with_suit 的执行过程中抛出异常时,本方法会立即执行,并且异常的三个参数也会传进来。下面我们自定义一个上下文管理器,并可以通过一个参数设定是否要忽略异常:
class Ctx(object): def init(self,ign=None): self.ign = ign def enter(self): pass def exit(self,exc_type,exc_val,exc_tb): return self.ign
with Ctx(True): raise TypeError('Ignored?')
with Ctx(False): raise TypeError('Ignored?')
Traceback (most recent call last): File "<pyshell#28>", line 2, in
raise TypeError('Ignored?') TypeError: Ignored? 如果在本方法中又引发了新的异常,那么这个新异常将覆盖掉 with_suit 中的异常