我今天第一次看到Python with
声明。我已经使用Python几个月了,甚至不知道它的存在!鉴于其地位有点模糊,我认为值得问:
with
语句旨在用于?try..finally
而不是with
?with
语句通过在所谓的context managers中封装常见的准备和清理任务来简化异常处理。更多细节可以在PEP 343找到。例如,open
语句本身就是一个上下文管理器,它允许你打开一个文件,只要执行在你使用它的with
语句的上下文中就保持打开,并在你离开时立即关闭它。上下文,无论你是因为异常还是在常规控制流程中都离开了它。因此,with
语句可以类似于C ++中的RAII pattern使用:某些资源由with
语句获取,并在您离开with
上下文时释放。with open(filename) as fp:
打开文件,使用with lock:
获取锁(其中lock
是threading.Lock
的一个实例)。您还可以使用contextmanager
中的contextlib
装饰器构建自己的上下文管理器。例如,当我必须暂时更改当前目录然后返回到我所在的位置时,我经常使用它:
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
重定向到其他文件句柄并稍后恢复它们的示例:
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"
最后,另一个创建临时文件夹并在离开上下文时清理它的示例:
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
在python中,通常“with”语句用于打开文件,处理文件中存在的数据,还可以在不调用close()方法的情况下关闭文件。 “with”语句通过提供清理活动使异常处理更简单。
一般形式:
with open(“file name”, “mode”) as file-var:
processing statements
注意:无需通过在file-var.close()上调用close()来关闭文件
我建议两个有趣的讲座:
1. with
语句用于使用上下文管理器定义的方法包装块的执行。这允许封装常见的try...except...finally
使用模式以便于重用。
你可以这样做:
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()
或(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
3.我在这里看不到任何反模式。 引用Dive into Python:
试试......最终是好的。与更好。
4.我猜这与程序员习惯使用其他语言的try..catch..finally
语句有关。
Python with
语句是C ++中常用的Resource Acquisition Is Initialization
习语的内置语言支持。它旨在允许安全地获取和释放操作系统资源。
with
语句在范围/块中创建资源。您使用块中的资源编写代码。当块退出时,无论块中代码的结果如何(即块是正常退出还是由于异常),都会干净地释放资源。
Python库中的许多资源遵循with
语句所要求的协议,因此可以与开箱即用的方法一起使用。但是,任何人都可以通过实施记录良好的协议来创建可以在with语句中使用的资源:PEP 0343
每当您获得必须明确放弃的应用程序中的资源(例如文件,网络连接,锁等)时,请使用它。
反模式的一个例子可能是在循环中使用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)
第一种方法是打开和关闭每个row
的文件,这可能会导致性能问题,而第二种方式是打开并关闭文件一次。
再次为了完整性,我将为with
语句添加我最有用的用例。
我做了很多科学计算和一些活动,我需要Decimal
库进行任意精度计算。我的代码的某些部分需要高精度,对于大多数其他部分我需要较低的精度。
我将默认精度设置为较低的数字,然后使用with
为某些部分获得更精确的答案:
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中经常使用它,需要对大数字进行除法,从而得到形式因子。进行基因组规模计算时,必须注意舍入和溢出错误。
请参阅PEP 343 - The 'with' statement,最后有一个示例部分。
...新的语句“with”到Python语言,可以分解try / finally语句的标准用法。
第1,2和3点得到了相当好的覆盖:
4:它是相对较新的,仅在python2.6 +中可用(或使用from __future__ import with_statement
的python2.5)
with语句适用于所谓的上下文管理器:
http://docs.python.org/release/2.5.2/lib/typecontextmanager.html
这个想法是通过在离开'with'块之后进行必要的清理来简化异常处理。一些python内置函数已经作为上下文管理器工作。
开箱即用支持的另一个例子,当你习惯于内置open()
行为的方式时,可能有点令人困惑的一个例子是热门数据库模块的connection
对象,例如:
connection
对象是上下文管理器,因此可以在with-statement
中使用开箱即用,但是当使用上述注释时:
当
with-block
完成时,无论是否有异常,连接都不会关闭。如果with-block
以异常结束,则回滚事务,否则提交事务。
这意味着程序员必须小心自己关闭连接,但允许获取连接,并在多个with-statements
中使用它,如psycopg2 docs所示:
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()
在上面的例子中,你会注意到cursor
的psycopg2
对象也是上下文管理器。从有关行为的相关文档:
当
cursor
退出with-block
时,它会关闭,释放最终与之相关的任何资源。交易状态不受影响。