python“with”语句是为什么devise的?

今天我第一次碰到Python。 我已经使用了Python几个月,甚至不知道它的存在! 鉴于其有些模糊的地位,我认为这值得提问:

  1. 什么是Python语句devise用于?
  2. 你用它做什么?
  3. 是否有任何我需要注意的问题,或与其使用相关的常见反模式? 任何情况下,最好使用try..finallywith
  4. 为什么它不被广泛使用?
  5. 哪些标准库类与它兼容?
  1. 我相信这已经被我之前的其他用户所回答了,所以我只是为了完整而添加它: with语句通过在所谓的上下文pipe理器中封装常见的准备和清理任务来简化exception处理。 更多细节可以在PEP 343中find。 例如, open语句本身就是一个上下文pipe理器,它允许你打开一个文件,只要执行是在你使用它的with语句的上下文中就保持打开状态,一旦你离开它就closures它上下文,不pipe你是否因为例外或者在正常的控制stream程中而离开它。 with语句因此可以用类似于C ++中RAII模式的方式使用:有些资源是由with语句获取的with当你离开with语句时释放。

  2. 一些例子是:使用with open(filename) as fp: ,使用with lock:获取锁(其中lockthreading.Lock一个实例)。 你也可以使用contextmanager的上下文pipe理器来构build你自己的上下文pipe理器。 例如,当我不得不暂时改变当前目录,然后返回到我所在的位置时,我经常使用它:

     from contextlib import contextmanager import os @contextmanager def working_directory(path): current_dir = os.getcwd() os.chdir(path) try: yield finally: os.chdir(current_dir) with working_directory("data/stuff"): # do something within data/stuff # here I am back again in the original working directory 

    下面是另一个例子, sys.stdinsys.stdoutsys.stderr临时redirect到其他文件句柄,并在以后恢复:

     from contextlib import contextmanager import sys @contextmanager def redirected(**kwds): stream_names = ["stdin", "stdout", "stderr"] old_streams = {} try: for sname in stream_names: stream = kwds.get(sname, None) if stream is not None and stream != getattr(sys, sname): old_streams[sname] = getattr(sys, sname) setattr(sys, sname, stream) yield finally: for sname, stream in old_streams.iteritems(): setattr(sys, sname, stream) with redirected(stdout=open("/tmp/log.txt", "w")): # these print statements will go to /tmp/log.txt print "Test entry 1" print "Test entry 2" # back to the normal stdout print "Back to normal stdout again" 

    最后,另一个例子创build一个临时文件夹,并在离开上下文时清理它:

     from tempfile import mkdtemp from shutil import rmtree @contextmanager def temporary_dir(*args, **kwds): name = mkdtemp(*args, **kwds) try: yield name finally: shutil.rmtree(name) with temporary_dir() as dirname: # do whatever you want 

我会build议两个有趣的讲座:

  • PEP 343 “附带”声明
  • Effbot了解Python的“with”语句

1. with语句用于用上下文pipe理器定义的方法来封装块的执行。 这允许共同的try...except...finally使用模式被封装以方便重用。

2.你可以做一些事情:

 with open("foo.txt") as foo_file: data = foo_file.read() 

要么

 from contextlib import nested with nested(A(), B(), C()) as (X, Y, Z): do_something() 

OR(Python 3.1)

 with open('data') as input_file, open('result', 'w') as output_file: for line in input_file: output_file.write(parse(line)) 

要么

 lock = threading.Lock() with lock: # Critical section of code 

我在这里看不到任何反模式。
引用潜水到Python :

试试..最后是好的。 与更好。

4.我想这与程序员习惯使用try..catch..finally来自其他语言的语句有关。

Python with语句是C ++中常用的Resource Acquisition Is Initialization习语的内置语言支持。 它旨在允许安全地获取和释放操作系统资源。

with语句在范围/块中创build资源。 你使用块内的资源编写你的代码。 当数据块退出时,无论块中代码的结果如何(即数据块是正常存在还是exception),干净地释放资源。

Python库中的许多资源符合with语句所要求的协议,因此可以在开箱即用的情况下使用。 然而,任何人都可以通过执行良好的文档化协议来制作可用于语句的资源: PEP 0343

当你在应用程序中获取必须明确放弃的资源(如文件,networking连接,锁等)时使用它。

反模式的一个例子可能是在循环内部使用with在循环外部使用with会更高效

例如

 for row in lines: with open("outfile","a") as f: f.write(row) 

VS

 with open("outfile","a") as f: for row in lines: f.write(row) 

第一种方法是打开和closures文件的每一row可能会导致性能问题相比,第二种方式打开和closures文件一次。

再次为了完整性,我将为语句添加最有用的用例。

我做了大量的科学计算和一些活动,我需要Decimal库进行任意的精度计算。 我的代码的一部分我需要高精度和大多数其他部分我需要更less的精度。

我把我的默认精度设置为一个较低的数字,然后用于获得一些更精确的答案:

 from decimal import localcontext with localcontext() as ctx: ctx.prec = 42 # Perform a high precision calculation s = calculate_something() s = +s # Round the final result back to the default precision 

我使用Hypergeometric Test进行了很多testing,这需要大量的数据分解。 当你做基因组比例计算时,你必须小心四舍五入和溢出错误。

见PEP 343 – 'with'语句 ,最后有一个例子部分。

…与Python语言的新语句“使”能够将try / finally语句的标准用法分解出来。

with语句与所谓的上下文pipe理器一起工作:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

这个想法是通过在离开'with'块后进行必要的清理来简化exception处理。 一些python内置函数已经作为上下文pipe理器工作。

第1,2和3点相当好地覆盖:

4:它比较新,只有在python2.6 +(或者from __future__ import with_statement使用python2.5)

开箱即用支持的另一个例子,当您习惯了内置open()行为的方式时,可能会有点莫名其妙,这是常用数据库模块的connection对象,例如:

  • sqlite3的
  • psycopg2
  • cx_oracle

connection对象是上下文pipe理器,因此可以在with-statement中直接使用,但是当使用上面的注释时:

with-block完成时,无论是否有exception, 连接都不会closures 。 在with-block完成exception的情况下,事务被回滚,否则事务被提交。

这意味着程序员必须小心地closures连接本身,但允许获取连接,并在多个with-statements使用它,如psycopg2文档中所示:

 conn = psycopg2.connect(DSN) with conn: with conn.cursor() as curs: curs.execute(SQL1) with conn: with conn.cursor() as curs: curs.execute(SQL2) conn.close() 

在上面的例子中,你会注意到psycopg2cursor对象也是上下文pipe理器。 从行为的相关文件:

当一个cursor退出时,它被closures,释放最终与之关联的资源。 交易状态不受影响。