Python中with...as...

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

try:    f =open('xxx')dosomethingexcept:dosomethingfinally:    f.close()

1

2

3

4

5

6

7

1

2

3

4

5

6

7

其实不止一次在网上看到有这么写的了,这个是错的。

首先正确的如下:

try:    f = open('xxx')except:    print'fail to open'exit(-1)try:dosomethingexcept:dosomethingfinally:    f.close()

1

2

3

4

5

6

7

8

9

10

11

1

2

3

4

5

6

7

8

9

10

11

很麻烦不是么,但正确的方法就是这么写。

我们为什么要写finally,是因为防止程序抛出异常最后不能关闭文件,但是需要关闭文件有一个前提就是文件已经打开了。

在第一段错误代码中,如果异常发生在f=open(‘xxx’)的时候,比如文件不存在,立马就可以知道执行f.close()是没有意义的。改正后的解决方案就是第二段代码。

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

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

setthings uptry:dosomethingfinally:    tear things down

1

2

3

4

5

1

2

3

4

5

这东西是个常见结构,比如文件打开,set things up就表示f=open(‘xxx’),tear things

down就表示f.close()。在比如像多线程锁,资源请求,最终都有一个释放的需求。Try…finally结构保证了tear things

down这一段永远都会执行,即使上面do something得工作没有完全执行。

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

defcontrolled_execution(callback):set things uptry:        callback(thing)finally:        tear things downdefmy_function(thing):do somethingcontrolled_execution(my_function)

1

2

3

4

5

6

7

8

9

10

11

1

2

3

4

5

6

7

8

9

10

11

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

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

defcontrolled_execution():set things uptry:yieldthingfinally:        tear things downforthingincontrolled_execution():    do somethingwiththing

1

2

3

4

5

6

7

8

9

1

2

3

4

5

6

7

8

9

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

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

classcontrolled_execution:def__enter__(self):set things upreturnthingdef__exit__(self, type, value, traceback):tear things downwithcontrolled_execution()asthing:        do something

1

2

3

4

5

6

7

8

9

1

2

3

4

5

6

7

8

9

在这里,python使用了with-as的语法。当python执行这一句时,会调用__enter__函数,然后把该函数return的值传给as后指定的变量。之后,python会执行下面do something的语句块。最后不论在该语句块出现了什么异常,都会在离开时执行__exit__。

另外,__exit__除了用于tear things down,还可以进行异常的监控和处理,注意后几个参数。要跳过一个异常,只需要返回该函数True即可。下面的样例代码跳过了所有的TypeError,而让其他异常正常抛出。

def__exit__(self, type, value, traceback):returnisinstance(value, TypeError)

1

2

1

2

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

>>> f =open("x.txt")>>> f>>> f.__enter__()>>> f.read(1)'X'>>> f.__exit__(None, None, None)>>> f.read(1)Traceback (most recent calllast):    File"",line1,inValueError: I/O operationonclosedfile

1

2

3

4

5

6

7

8

9

10

11

12

1

2

3

4

5

6

7

8

9

10

11

12

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

withopen("x.txt")asf:data= f.read()dosomething withdata

1

2

3

1

2

3

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

withopen("x.txt")asf1,open('xxx.txt')asf2:dosomethingwithf1,f2

1

2

1

2

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

try:withopen("a.txt")asf :dosomethingexcept xxxError:dosomething aboutexception

1

2

3

4

5

1

2

3

4

5

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

with-block等价于

try:        执行 __enter__的内容        执行 with_block.finally:        执行 __exit__内容

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Python’s with statement provides a very convenient way of...
    尼古拉斯_特仑苏阅读 463评论 0 1
  • 首先自黑,我之前一直用这种二逼写法: 不仅粗鲁,而且我们会发现,在文件不存在的情况下,f.close()没有意义。...
    李响同學阅读 1,740评论 4 11
  • “你的,志雄君,有什么目标?”丁老师拍拍身边日本籍学生秦川志雄的肩膀。 “认真完成作业,还有,还有不给阿姨添麻烦。...
    幸福梦中仙阅读 195评论 0 0
  • 这个题目本来是放假的时候我给,全校的学生布置的。因为在会议上我说了,让每个班主任都给学生布置三篇作文,内容就是春节...
    lygly9阅读 271评论 0 1
  • 6-20星期一 今早闹钟响了两遍才起来,明晃晃的【今天不去跑】的想法充斥着我的意识。我用几乎就妥协了,最后战胜这一...
    魏雨self阅读 169评论 0 0