python“with”语句是为什么devise的?
今天我第一次碰到Python。 我已经使用了Python几个月,甚至不知道它的存在! 鉴于其有些模糊的地位,我认为这值得提问:
- 什么是Python语句devise用于?
- 你用它做什么?
- 是否有任何我需要注意的问题,或与其使用相关的常见反模式? 任何情况下,最好使用
try..finally
比with
? - 为什么它不被广泛使用?
- 哪些标准库类与它兼容?
-
我相信这已经被我之前的其他用户所回答了,所以我只是为了完整而添加它:
with
语句通过在所谓的上下文pipe理器中封装常见的准备和清理任务来简化exception处理。 更多细节可以在PEP 343中find。 例如,open
语句本身就是一个上下文pipe理器,它允许你打开一个文件,只要执行是在你使用它的with
语句的上下文中就保持打开状态,一旦你离开它就closures它上下文,不pipe你是否因为例外或者在正常的控制stream程中而离开它。with
语句因此可以用类似于C ++中RAII模式的方式使用:有些资源是由with
语句获取的with
当你离开with
语句时释放。 -
一些例子是:使用
with open(filename) as fp:
,使用with lock:
获取锁(其中lock
是threading.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.stdin
,sys.stdout
和sys.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()
在上面的例子中,你会注意到psycopg2
的cursor
对象也是上下文pipe理器。 从行为的相关文件:
当一个
cursor
退出时,它被closures,释放最终与之关联的资源。 交易状态不受影响。