总览
在 Python 中,您须要通过关上文件来拜访文件。您能够应用 open()
函数来实现。Open 返回一个文件对象,该文件对象具备用于获取无关已关上文件的信息和对其进行操作的办法和属性。
with 语句
应用“with”语句,使代码简洁,解决异样也更优雅。
“with 语句通过封装罕用的筹备工作和革除工作来简化异样解决。”
此外,它将主动敞开文件。with 语句提供了一种确保始终应用清理的办法。
如果没有 with 语句,咱们将编写如下内容:
file = open("welcome.txt")
data = file.read()
print(data)
file.close() # 文件用完肯定要敞开
with 语句用法
‘with’ 语句是一个新的控制流构造,其根本构造为:
with expression [as variable]:
with-block
应用 with 关上文件非常简单:应用open(filename) as file:
with open("welcome.txt") as file: # file 做为对文件对象的援用
data = file.read()
# 应用 data 做点啥
在写入模式下关上output.txt
with open('output.txt', 'w') as file: # 输入到 file
file.write('Hi there!')
留神,咱们不用编写 file.close()
。会被主动调用。
原理
‘ with ‘ 语句简化了以前应用 try...finally
块来确保执行革除代码的代码。在本节中,我将探讨通常应用的语句。在下一节中,我将查看实现细节,并阐明如何编写用于此语句的对象。
with 前面的表达式需反对 上下文治理协定 (即,__enter__()
和__exit__()
办法)。
with expression [as variable]:
with-block
在执行 with-block 之前调用对象的__enter __() 办法,因而能够运行 setup 设置代码。能够能过 as 把表达式后果绑定到变量 variable(留神这里不是赋值到变量 variable)。
with 块的执行实现后,即便该块引发了异样,该对象的 __exit__()
办法也会被调用,因而能够运行清理代码。
要在 Python 2.5 中启用该语句,您须要在模块中增加以下指令:
from __future__ import with_statement
该语句将始终在 Python 2.6 中启用。
当初,一些规范的 Python 对象反对 上下文治理协定,并且能够与 ‘with’ 语句一起应用。文件对象即是其中之一:
with open('/etc/passwd', 'r') as f:
for line in f:
print line
... 更多 ...
执行此语句后,即便 for 循环在代码块中途出现异常,f 中的文件对象也将主动敞开。
留神: 在这种状况下,f
是 open()
创立的同一对象,因为 file.__enter__()
返回 self
。
threading
模块的锁和条件变量也反对 ‘with’ 语句:
lock = threading.Lock()
with lock:
# 代码临界区
...
该锁在执行 with 块之前获取,并在该块实现后始终开释。
decimal
模块中 的新 localcontext()
函数使保留和还原以后 decimal
上下文变得容易,它封装了计算所需的精度和舍入特色:
from decimal import Decimal, Context, localcontext
# 显示默认精度:28 位数字
v = Decimal('578')
print v.sqrt()
with localcontext(Context(prec=16)):
# 本代码块中应用 16 位精度.
# 原始上下文将在退出块后复原.
print(v.sqrt())
编写上下文管理器
在幕后,with
语句相当简单。大多数人只会在与现有对象一起应用 ‘with’,并且不须要晓得这些详细信息,如果您想让本人写的类也反对 with 语句,那就须要理解上下文管理器了。
上下文治理协定的高级解释是:
- 该表达式将被求值并应产生一个称为 “context manager” 的对象。上下文管理器必须蕴含 __enter__() 和 __exit__() 办法。
- 上下文管理器的 __enter__() 办法被调用。返回的值调配给 var。如果不存在
as var
子句,则仅抛弃该值。 - with 块中的代码被执行。
- 如果 with 块引发异样,则应用异样详细信息调用
__exit__(type,value,traceback)
,该异样详细信息由sys.exc_info()
返回。该办法的返回值管制是否从新引发异样:任何False
值都会从新引发异样,True
会克制异样。通常很少须要克制异样,因为如果您这样做,蕴含 ‘with’ 语句的代码的作者将永远不会意识到任何谬误。 - 如果 with 块没有引发异样,则依然会调用
__exit__()
办法,此时参数 type,value 和 traceback 都是None
。
让咱们思考一个例子。我不会提供具体的代码,而只会概述反对事务的数据库所必须的办法。
(对于不相熟数据库术语的人:将对数据库的一组更改分组为一个事务。能够提交事务,这意味着将所有更改都写入数据库,也能够回滚,这意味着将所有更改都抛弃并删除。数据库未更改。无关更多信息,请参见任何数据库教科书。)
假如有一个代表数据库连贯的对象。咱们的指标是让用户编写如下代码:
db_connection = DatabaseConnection()
with db_connection as cursor:
cursor.execute('insert into ...')
cursor.execute('delete from ...')
# ... more operations ...
如果块中的代码完满运行,则应该提交事务;如果有异样,则应回滚事务。这是我假如的 DatabaseConnection 的根本接口:
class DatabaseConnection:
...
def __enter__ (self):
# Code to start a new transaction
cursor = self.cursor()
return cursor
该__enter __()办法是很简略的,只有到启动新的事务。对于此应用程序,后果光标对象将是有用的后果,因而该办法将返回它。而后,用户能够增加 as cursor
到其 with 语句中,以将游标绑定到变量名。
class DatabaseConnection:
# Database interface
def cursor (self):
"Returns a cursor object and starts a new transaction"
def commit (self):
"Commits current transaction"
def rollback (self):
"Rolls back current transaction"
该__exit __()办法有点简单,该办法必须查看是否产生异样。如果没有异样,则提交事务。如果存在异样,则事务将回滚。
在上面的代码中,执行会从函数的开端开始,并返回默认值 None
。None
为假,因而将主动从新引发异样。如果须要,能够更加明确,并 在标记的地位增加 return 语句。
class DatabaseConnection:
...
def __exit__ (self, type, value, tb):
if tb is None:
# No exception, so commit
self.commit()
else:
# Exception occurred, so rollback.
self.rollback()
# return False
contextlib 模块
contextlib 模块提供了一些性能和装璜器,这些性能和装璜器对于编写与 ‘with’ 语句一起应用的对象很有用。
装璜器称为 contextmanager,它使您能够编写一个生成器函数,而不必定义一个新类。生成器应恰好产生一个值。直到 yield 的代码 将作为__enter __()办法执行,并且 yield 的值将是该办法的返回值,该返回值将绑定到 ’ with ‘ 语句的 as 子句中的变量(如果有)。屈从后的代码将在 __exit __()办法中执行。块中引发的任何异样都将由 yield 语句引发。
上一节中的数据库示例能够应用以下装璜器编写为:
from contextlib import contextmanager
@contextmanager
def db_transaction (connection):
cursor = connection.cursor()
try:
yield cursor
except:
connection.rollback()
raise
else:
connection.commit()
db = DatabaseConnection()
with db_transaction(db) as cursor:
该 contextlib 模块还具备嵌套(MGR1,MGR2,…)性能联合了一些上下文管理器,所以你不须要写嵌套“不与”语句。在此示例中,单个 ’ with ‘ 语句既启动数据库事务并获取线程锁:
lock = threading.Lock()
with nested (db_transaction(db), lock) as (cursor, locked):
最初,Closeing(object)函数返回 object,以便能够将其绑定到变量,并 object.close()
在块的开端调用。
import urllib, sys
from contextlib import closing
with closing(urllib.urlopen('http://bixuebihui.com')) as f:
for line in f:
sys.stdout.write(line)
参考:
https://docs.python.org/2.5/w…