[转]Python中的with…as…

先说明一个常见问题,文件打开:


1

2

3

4

5

6

7

try:

    f = open(‘xxx‘)

    do something

except:

    do something

finally:

    f.close()

其实我个人不止一次在网上看到有这么写的了,这个是错的。
首先正确的如下:


1

2

3

4

5

6

7

8

9

10

11

try:

    f = open(‘xxx‘)

except:

    print ‘fail to open‘

    exit(-1)

try:

    do something

except:

    do something

finally:

    f.close()

很麻烦不是么,但正确的方法就是这么写。
我们为什么要写finally,是因为防止程序抛出异常最后不能关闭文件,但是需要关闭文件有一个前提就是文件已经打开了。
在第一段错误代码中,如果异常发生在f=open(‘xxx’)的时候,比如文件不存在,立马就可以知道执行f.close()是没有意义的。改正后的解决方案就是第二段代码。

好了言归正转,开始讨论with语法。

首先我们从下面这个问题谈起,try-finally的语法结构:


1

2

3

4

5

set things up

try:

    do something

finally:

    tear things down

这东西是个常见结构,比如文件打开,set things up就表示f=open(‘xxx‘)tear things down就表示f.close()。在比如像多线程锁,资源请求,最终都有一个释放的需求。Try…finally结构保证了tear things down这一段永远都会执行,即使上面do something得工作没有完全执行。

如果经常用这种结构,我们首先可以采取一个较为优雅的办法,封装!


1

2

3

4

5

6

7

8

9

10

11

def controlled_execution(callback):

    set things up

    try:

        callback(thing)

    finally:

        tear things down

def my_function(thing):

    do something

controlled_execution(my_function)

封装是一个支持代码重用的好办法,但是这个办法很dirty,特别是当do something中有修改一些local variables的时候(变成函数调用,少不了带来变量作用域上的麻烦)。

另一个办法是使用生成器,但是只需要生成一次数据,我们用for-in结构去调用他:


1

2

3

4

5

6

7

8

9

def controlled_execution():

    set things up

    try:

        yield thing

    finally:

        tear things down

        

for thing in controlled_execution():

    do something with thing

因为thing只有一个,所以yield语句只需要执行一次。当然,从代码可读性也就是优雅的角度来说这简直是糟糕透了。我们在确定for循环只执行一次的情况下依然使用了for循环,这代码给不知道的人看一定很难理解这里的循环是什么个道理。

最终的python-dev团队的解决方案。(python 2.5以后增加了with表达式的语法)


1

2

3

4

5

6

7

8

9

class controlled_execution:

    def __enter__(self):

        set things up

        return thing

    def __exit__(self, type, value, traceback):

        tear things down

        

with controlled_execution() as thing:

        do something

在这里,python使用了with-as的语法。当python执行这一句时,会调用__enter__函数,然后把该函数return的值传给as后指定的变量。之后,python会执行下面do something的语句块。最后不论在该语句块出现了什么异常,都会在离开时执行__exit__。
另外,__exit__除了用于tear things down,还可以进行异常的监控和处理,注意后几个参数。要跳过一个异常,只需要返回该函数True即可。下面的样例代码跳过了所有的TypeError,而让其他异常正常抛出。


1

2

def __exit__(self, type, value, traceback):

    return isinstance(value, TypeError)

在python2.5及以后,file对象已经写好了__enter__和__exit__函数,我们可以这样测试:


1

2

3

4

5

6

7

8

9

10

11

12

>>> f = open("x.txt")

>>> f

<open file ‘x.txt‘, mode ‘r‘ at 0x00AE82F0>

>>> f.__enter__()

<open file ‘x.txt‘, mode ‘r‘ at 0x00AE82F0>

>>> f.read(1)

‘X‘

>>> f.__exit__(None, None, None)

>>> f.read(1)

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

ValueError: I/O operation on closed file

之后,我们如果要打开文件并保证最后关闭他,只需要这么做:


1

2

3

with open("x.txt") as f:

    data = f.read()

    do something with data

如果有多个项,我们可以这么写:


1

2

with open("x.txt") as f1, open(‘xxx.txt‘) as f2:

    do something with f1,f2

上文说了__exit__函数可以进行部分异常的处理,如果我们不在这个函数中处理异常,他会正常抛出,这时候我们可以这样写(python 2.7及以上版本,之前的版本参考使用contextlib.nested这个库函数):


1

2

3

4

5

try:

    with open( "a.txt" ) as f :

        do something

except xxxError:

    do something about exception

总之,with-as表达式极大的简化了每次写finally的工作,这对保持代码的优雅性是有极大帮助的。

时间: 2024-10-22 01:25:11

[转]Python中的with…as…的相关文章

走入计算机的第四十天(python中sockserver模块)

一.Python中的sockserver模块 1.该模块与sock模块不同之处是该模块自动帮我们分装好了一些功能,让我们在编程的时候直接调用这些功能就可以了,节省了编程步骤. 2.如图所示 注释:上图为服务端设置 该模块的操作方法比较死板,我们只要会熟悉的使用他就可以了.

python中if __name__ == &#39;__main__&#39;:

Using a module's __name__ Example? 8.2.? Using a module's __name__ #!/usr/bin/python # Filename: using_name.py if __name__ == '__main__': print 'This program is being run by itself' else: print 'I am being imported from another module' Output $ pytho

关于Python中的yield

关于Python中的yield http://www.cnblogs.com/tqsummer/archive/2010/12/27/1917927.html http://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/ 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何类型,包括列表.元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器 迭代器是一个实现了迭代器协议

python中的那些“神器”

"武林至尊,宝刀屠龙,号令天下,莫敢不从,倚天不出,谁与争锋",这是神器.不过今天要说的python中的"神器"就没有这么厉害了,这里要说的"神器"其实就是名称里面带了个"器"的,如下: 列表解析器 迭代器 生成器 装饰器 列表解析器 现在遇到了这样一个问题需要解决:"有一个数字的列表,要求对该列表中的奇数乘以2,返回处理完成后的列表(不改变原来列表的顺序,仅对列表中的奇数乘以2)",比较传统的方法可能会是

Python中字符串格式化如何实现?

Python开发中字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存. This PEP proposes a new system for built-in string formatting operations, intended as a replacement for the existing '%' string formatting operator. 1.百分号方式 %[(na

python 中*args 和 **kwargs

简单的可以理解为python 中给函数传递的可变参数,args 是 列表的形式.kwargs 是 key,value的形式,也就是python 中的字典. *args 必须出现在**kwargs 的前边,否则会抛异常. 1 def test(*args, **kwargs): 2 print args 3 print kwargs 1 if __name__ == '__main__': 2 print '---test 1---' 3 test(1, 2, 3) 4 print '---tes

python中super出现的TypeError: must be type, not classobj 原因及解决

执行一下代码,出现错误,TypeError: must be type, not classobj class A():    def __init__(self):        print("Enter A")        print("Leave A") class B(A):    def __init__(self):        print("Enter B")        super(B, self).__init__()  

python中的切片问题

对于在一个字符串中选取几个字符,不同的语言有不同的解决方案,python 中就有了切片的方法.    在list中,如下:     s=list(range(1,101))    如果想要选取偶数个数字(或者选取偶数),可以用循环的方法:但是方法臃肿,比较"笨"    但是python中给出的切片方法是更加的优雅的,如下:    L=list(range(1,101))    print(L[0])    print(L[0:10])#输出结果是[1, 2, 3, 4, 5, 6, 7

Python学习-16.Python中的错误处理

虽然叫错误,但跟 C# 中的异常是一回事.只不过 Python 中叫错误(Error)而 C# 中叫异常(Exception). 先手工产生一个异常: 1 file = open('','r') 上面一句由于路径是空路径,因此文件肯定是不存在的,执行这一句会引发 FileNotFoundError 这个错误. 既然是错误的,程序也停止了,这是我们不希望的,因此得想办法处理一下. 在 Python 中,异常处理使用 try.except.finally 这三个关键字. 修改代码如下: 1 path

python中各种基础类型的转换,二进制,八进制,十进制,十六进制

python中所有类型都是作为对象的形式来存在的. 在python中没有char型,只有字符串类型,这样我们可能将char型转换为整型时极不方便,但是python已经提供了这些转换的内置函数. python 中除了整型,其他进制的只能用字符串来表示 1 int() 可以将 二进制,八进制,十六进制转换成十进制整型 >>> int('1111', 2) 15 >>> int('f', 16) 15 >>> int('17', 8) 15 2 chr()