Python中的With语句

总览

在Python中,您须要经过打开文件来访问文件。您可使用 open()函数来实现。Open 返回一个文件对象,该文件对象具备用于获取有关已打开文件的信息和对其进行操做的方法和属性。html

with 语句

使用 “with” 语句,使代码简洁,处理异常也更优雅。python

“with语句经过封装经常使用的准备工做和清除任务来简化异常处理。”数据库

此外,它将自动关闭文件。with 语句提供了一种确保始终使用清理的方法。express

若是没有 with 语句,咱们将编写以下内容:函数

file = open("welcome.txt")
data = file.read()
print(data)
file.close()  # 文件用完必定要关闭

with 语句用法

'with' 语句是一个新的控制流结构,其基本结构为:ui

with expression [as variable]:
    with-block

使用 with 打开文件很是简单:使用open(filename) as file:url

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()。会被自动调用。code

原理

' with '语句简化了之前使用try...finally块来确保执行清除代码的代码。在本节中,我将讨论一般使用的语句。在下一节中,我将检查实现细节,并说明如何编写用于此语句的对象。htm

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中的文件对象也将自动关闭。

注意: 在这种状况下,fopen() 建立的同一对象 ,由于 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 __()方法有点复杂,该方法必须检查是否发生异常。若是没有异常,则提交事务。若是存在异常,则事务将回滚。

在下面的代码中,执行会从函数的末尾开始,并返回默认值NoneNone为假,所以将自动从新引起异常。若是须要,能够更加明确,并 在标记的位置添加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...

相关文章
相关标签/搜索