理解Python中的with…as…语法
使用语言的好特性,而不是那些糟糕的特性————不知道谁说的
好久不学习python的语法了,上次去面试,和面试官聊到了python中的with-as statement(也称context manager),挺感兴趣的,这两天学习了一番,收获颇丰在此分享。
先说明一个常见问题,文件打开:
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的工作,这对保持代码的优雅性是有极大帮助的。
感谢以下参考资料:
stackoverflow: Catching an exception while using a Python ‘with’ statement
Understanding Python’s “with” statement
python docs:
http://docs.python.org/2/reference/compound_stmts.html#with
http://docs.python.org/2/reference/datamodel.html#context-managers
http://docs.python.org/2/library/contextlib.html#contextlib.nested
相关推荐
with open('example.yaml', 'r') as file: data = yaml.safe_load(file) ``` 这段代码会打开名为'example.yaml'的文件,然后使用`safe_load`函数将YAML数据转换为Python对象,如字典、列表等。 生成YAML文件同样...
with open('audio_file.wav', 'rb') as audio_file: audio_data = audio_file.read() # 进行语音识别 hyp = decoder.decode_raw(audio_data) print(hyp.hypstr) ``` 这段代码首先加载了中文语言模型和字典,然后...
with Reader('path/to/GeoLite2-City.mmdb') as database: response = database.city('1.2.3.4') print(response.country.name) ``` 在这段代码中,`Reader`类创建了一个数据库读取器,然后我们查询了一个IP地址...
在IT行业中,Python是一种广泛应用的高级编程语言,以其简洁、易读的语法和丰富的库支持而备受青睐。本文将深入探讨“fbgbp-0.0.4.tar.gz”这个Python库,它作为开发工具,可能包含了一系列的功能模块,旨在为Python...
使用with语句能确保文件正确关闭:`with open('filename', 'r') as file:`。 5. 正则表达式: Python的`re`模块提供正则表达式支持,可以用来进行复杂的文本匹配和查找。例如,`import re; pattern = re.compile(r...
Python是一种广泛使用的高级编程语言,以其易读性、简洁的语法和强大的功能而闻名。在IT行业中,Python被广泛应用于数据分析、机器学习、网络爬虫、Web开发等多个领域。"python-hashes"这个主题可能涉及到Python中的...
with open('local_file.txt', 'rb') as f: new_file = api_client.files().create( owner_uuid=new_collection.uuid, data=f, content_type='text/plain').execute() ``` 通过以上介绍,我们可以看到 `arvados-...
`PyYAML`是一个Python库,它提供了对YAML语法的全面支持,包括解析YAML文档到Python对象以及将Python对象序列化为YAML格式。 **1. YAML语言简介** YAML是一种轻量级的数据序列化语言,它的设计目标是人类可读性和...
with open('data.yaml', 'r') as f: data = syaml.safe_load(f) # 将Python对象写入YAML文件 new_data = {...} with open('new_data.yaml', 'w') as f: syaml.dump(new_data, f) ``` 标签“Python库”进一步确认...
async with Client('localhost', 11211) as client: await client.set('key', 'value') result = await client.get('key') print(result) asyncio.run(main()) ``` 以上代码展示了如何连接到本地的Memcached...
with open('output.yaml', 'w') as f: yaml.dump(new_data, f) ``` **5. YAML与JSON的对比** 虽然JSON(JavaScript Object Notation)也是一种常见的数据交换格式,但YAML提供了更丰富的数据类型和更人性化的语法...
with open('datamatrix.png', 'wb') as f: f.write(matrix.pil_image().tostring()) ``` 这个例子展示了如何使用`qdatamatrix`库生成Data Matrix图像并保存为PNG文件。 总结起来,`python_qdatamatrix-0.1.10-py2....
在`smbus2_asyncio-0.0.3`这个版本中,我们可以看到库的核心功能是将I2C操作异步化,使得开发者可以利用Python的async/await语法,实现非阻塞的I2C通信,提高程序执行效率。这对于需要处理多个并发I2C任务的项目来说...
Python是一种高级编程语言,以其简洁明了的语法和强大的功能而受到广泛的欢迎。这份"python-cheat-sheet-basic.pdf"是一个Python基础知识的速查表,涵盖了读取文件、字符串操作、数值类型与数学运算以及列表操作等多...
with backend.cursor() as cursor: cursor.execute("SELECT * FROM my_table") for row in cursor.fetchall(): print(row) ``` Shillelagh还支持SQLAlchemy风格的查询语法,使其更易于使用和理解。 **总结** `...
async with aquests.get(url) as response: return await response.text() # 启动事件循环 asyncio.run(fetch('http://example.com')) ``` aquests库还提供了其他高级特性,如支持POST、PUT、DELETE等多种HTTP...
这些建议涵盖了Python编程中的各个方面,从基础语法到高级特性都有涉及。 ### 二、重要知识点概览 #### 1. 使用列表推导式代替循环 列表推导式是一种更为简洁且高效的创建列表的方式。例如,可以使用列表推导式来...
在IT行业中,Python是一种广泛应用的高级编程语言,以其简洁、易读的语法和丰富的库生态而闻名。在处理给定的资源"yAuth-0.0.1.tar.gz"时,我们可以了解到这是一款名为yAuth的Python库的早期版本0.0.1。这个库可能...
with tracer.start_span("operation_name") as span: # 在这里执行你的代码 pass ``` spanclient库通常与其他分布式追踪系统如Jaeger、Zipkin或OpenTelemetry兼容。这些系统提供了可视化界面,可以帮助开发者查看...
在IT行业中,Python是一种广泛应用的高级编程语言,以其简洁、易读的语法和丰富的库支持而备受青睐。本文将深入探讨“Python库 | PqrUploadModule-0.0.10.tar.gz”这一资源,主要关注它是什么,如何使用,以及在...