`
reb12345reb
  • 浏览: 50177 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Python-With...As语法

 
阅读更多

理解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

Tags : python
分享到:
评论

相关推荐

    Python库 | types-PyYAML-5.4.11.tar.gz

    with open('example.yaml', 'r') as file: data = yaml.safe_load(file) ``` 这段代码会打开名为'example.yaml'的文件,然后使用`safe_load`函数将YAML数据转换为Python对象,如字典、列表等。 生成YAML文件同样...

    cmusphinx-zh-cn-5.2.tar.gz

    with open('audio_file.wav', 'rb') as audio_file: audio_data = audio_file.read() # 进行语音识别 hyp = decoder.decode_raw(audio_data) print(hyp.hypstr) ``` 这段代码首先加载了中文语言模型和字典,然后...

    PyPI 官网下载 | python-geoip-geolite2-yplan-2016.105.tar.gz

    with Reader('path/to/GeoLite2-City.mmdb') as database: response = database.city('1.2.3.4') print(response.country.name) ``` 在这段代码中,`Reader`类创建了一个数据库读取器,然后我们查询了一个IP地址...

    Python库 | fbgbp-0.0.4.tar.gz

    在IT行业中,Python是一种广泛应用的高级编程语言,以其简洁、易读的语法和丰富的库支持而备受青睐。本文将深入探讨“fbgbp-0.0.4.tar.gz”这个Python库,它作为开发工具,可能包含了一系列的功能模块,旨在为Python...

    python-用法.docx

    使用with语句能确保文件正确关闭:`with open('filename', 'r') as file:`。 5. 正则表达式: Python的`re`模块提供正则表达式支持,可以用来进行复杂的文本匹配和查找。例如,`import re; pattern = re.compile(r...

    python安装python-hashes.rar

    Python是一种广泛使用的高级编程语言,以其易读性、简洁的语法和强大的功能而闻名。在IT行业中,Python被广泛应用于数据分析、机器学习、网络爬虫、Web开发等多个领域。"python-hashes"这个主题可能涉及到Python中的...

    PyPI 官网下载 | arvados-python-client-1.3.1.20190402172810.tar.gz

    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-5.1.2.tar.gz

    `PyYAML`是一个Python库,它提供了对YAML语法的全面支持,包括解析YAML文档到Python对象以及将Python对象序列化为YAML格式。 **1. YAML语言简介** YAML是一种轻量级的数据序列化语言,它的设计目标是人类可读性和...

    PyPI 官网下载 | syaml-1.0.post1-py3-none-any.whl

    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库”进一步确认...

    AioMemcached-0.8.0-py2.py3-none-any.whl.zip

    async with Client('localhost', 11211) as client: await client.set('key', 'value') result = await client.get('key') print(result) asyncio.run(main()) ``` 以上代码展示了如何连接到本地的Memcached...

    PyYAML-3.12.tar.gz

    with open('output.yaml', 'w') as f: yaml.dump(new_data, f) ``` **5. YAML与JSON的对比** 虽然JSON(JavaScript Object Notation)也是一种常见的数据交换格式,但YAML提供了更丰富的数据类型和更人性化的语法...

    Python库 | python_qdatamatrix-0.1.10-py2.py3-none-any.whl

    with open('datamatrix.png', 'wb') as f: f.write(matrix.pil_image().tostring()) ``` 这个例子展示了如何使用`qdatamatrix`库生成Data Matrix图像并保存为PNG文件。 总结起来,`python_qdatamatrix-0.1.10-py2....

    PyPI 官网下载 | smbus2_asyncio-0.0.3.tar.gz

    在`smbus2_asyncio-0.0.3`这个版本中,我们可以看到库的核心功能是将I2C操作异步化,使得开发者可以利用Python的async/await语法,实现非阻塞的I2C通信,提高程序执行效率。这对于需要处理多个并发I2C任务的项目来说...

    python-cheat-sheet-basic.pdf

    Python是一种高级编程语言,以其简洁明了的语法和强大的功能而受到广泛的欢迎。这份"python-cheat-sheet-basic.pdf"是一个Python基础知识的速查表,涵盖了读取文件、字符串操作、数值类型与数学运算以及列表操作等多...

    PyPI 官网下载 | shillelagh-0.6.0-py2.py3-none-any.whl

    with backend.cursor() as cursor: cursor.execute("SELECT * FROM my_table") for row in cursor.fetchall(): print(row) ``` Shillelagh还支持SQLAlchemy风格的查询语法,使其更易于使用和理解。 **总结** `...

    Python库 | aquests-0.29.11.6-py3-none-any.whl

    async with aquests.get(url) as response: return await response.text() # 启动事件循环 asyncio.run(fetch('http://example.com')) ``` aquests库还提供了其他高级特性,如支持POST、PUT、DELETE等多种HTTP...

    Слаткин -- Секреты Python -- 2016.pdf

    这些建议涵盖了Python编程中的各个方面,从基础语法到高级特性都有涉及。 ### 二、重要知识点概览 #### 1. 使用列表推导式代替循环 列表推导式是一种更为简洁且高效的创建列表的方式。例如,可以使用列表推导式来...

    Python库 | yAuth-0.0.1.tar.gz

    在IT行业中,Python是一种广泛应用的高级编程语言,以其简洁、易读的语法和丰富的库生态而闻名。在处理给定的资源"yAuth-0.0.1.tar.gz"时,我们可以了解到这是一款名为yAuth的Python库的早期版本0.0.1。这个库可能...

    Python库 | spanclient-0.2.1-py3-none-any.whl

    with tracer.start_span("operation_name") as span: # 在这里执行你的代码 pass ``` spanclient库通常与其他分布式追踪系统如Jaeger、Zipkin或OpenTelemetry兼容。这些系统提供了可视化界面,可以帮助开发者查看...

    Python库 | PqrUploadModule-0.0.10.tar.gz

    在IT行业中,Python是一种广泛应用的高级编程语言,以其简洁、易读的语法和丰富的库支持而备受青睐。本文将深入探讨“Python库 | PqrUploadModule-0.0.10.tar.gz”这一资源,主要关注它是什么,如何使用,以及在...

Global site tag (gtag.js) - Google Analytics