[Python]_[初级]_[关于with语句的使用方法]

场景

1.在很多项目代码里, 经常会遇到语句with的使用, 这个with到底如何使用的呢, 它又会用在哪方面呢?

2.with语句是python里特殊的语句, 在Java,Object-C,C++里是没有的, 它是否是关键的语句, 没有这个语句是不是很多场景就没有替代方案了?

说明

0.with语句并不是必须的语句, 它的替代方案就是try/finally语句. 只不过with语句需要的对象比较特殊, 需要自带__enter__exit__实现,和C++的构造和析构函数很像, 在对象生命周期开始和结束时自动分别调用构造和析构函数. 它只是一个具备执行流程顺序的封装语句而已, 只是在资源创建销毁时使用, 起到精简代码的作用.

1.语法

with_stmt ::=  "with" with_item ("," with_item)* ":" suite
with_item ::=  expression ["as" target]

或者

with EXPR as VAR:
    BLOCK

python解析器在内部大概会解析为:

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

with内部执行顺序说明:

  • 1.在with_item里的上下文表达式执行获取一个context manager.
  • 2.加载context manager 力的__exit__() , 注意是加载, 不是调用.
  • 3.调用context manager里的__enter__函数.
  • 4.如果target或者说有as关键字, 那么__enter__方法的返回值赋值给target.
  • 5.执行suite
  • 6.context manager__exit__被调用, 如果有异常, 那么traceback会传递给__exit__, 否则传递None

2.with 语句目的就是为了提供一个标准的try/finally使用方式. 注意, 没有except, 该抛出的异常还是会抛出的. 它的重点在context manager上, 满足contextmanager protocol(协议)的对象称之为context manager. 这个协议对象必须实现了 __enter__()__exit__()方法.在标准库里, files, sockets, 和locks 对象都实现了这个协议.

比如

- file
- thread.LockType
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore
- threading.BoundedSemaphore

3.open() 的位置在 The Python Standard Library->Built in Functions 里. 如果打开失败, 那么会抛出一个 OSError 错误. with是捕抓不到这个异常的.

4.自定义contextmanager的方式有两种, 一种是定义一个类, 并实现__enter____exit__方法; 另外一种是使用@contextmanager来修饰generator函数, 这种比较方便, 只需要定义一个函数即可.

例子

以下例子列举了contextmanager的两种创建方式:


import io
from contextlib import contextmanager

class tranaction:
    def __init__(self, obj):
        print ("get a db handle")
        self.obj = obj

    def __enter__(self):
        print ("begin db tranaction")
        return self.obj

    def __exit__(self, *exc_info):
        if exc_info is None:
            print ("rollback db tranaction")
        else:
            print ("commit db tranaction")

@contextmanager
def TranactionDecorator(obj):
    print("begin db tranaction")
    try:
        yield None
    except Exception:
        print("do db rollback")
    else:
        print("commit db tranaction")

if __name__ == '__main__':

    print ("====== tranaction class ======")
    with tranaction(None):
        print ("do insert intot sql")

    print("====== Tranaction Generator1 ======")
    with TranactionDecorator(None):
        print("do insert intot sql-1")
        raise 1

    print("====== Tranaction Generator2 ======")
    with TranactionDecorator(None):
        print("do insert intot sql-2")

输出

====== tranaction class ======
get a db handle
begin db tranaction
do insert intot sql
commit db tranaction
====== Tranaction Generator1 ======
begin db tranaction
do insert intot sql-1
do db rollback
====== Tranaction Generator2 ======
begin db tranaction
do insert intot sql-2
commit db tranaction

参考

  1. PEP 343 – The “with” Statement

  2. Python document 里的 8.5. The with statement.

file object
An object exposing a file-oriented API (with methods such as read() or write()) to an underlying
resource. Depending on the way it was created, a file object can mediate access to a real
on-disk file or to another type of storage or communication device (for example standard 
input/output, in-memory buffers, sockets, pipes, etc.). File objects are also called file-like 
objects or streams.

There are actually three categories of file objects: raw binary files, buffered binary files 
and text files. Their interfaces are defined in the io module. The canonical way to 
create a file object is by using the open() function


generator
A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.

generator iterator
An object created by a generator function.

Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation).


常见的contextmanager修饰符实现方式:

class GeneratorContextManager(object):

   def __init__(self, gen):
       self.gen = gen

   def __enter__(self):
       try:
           return self.gen.next()
       except StopIteration:
           raise RuntimeError("generator didn't yield")

   def __exit__(self, type, value, traceback):
       if type is None:
           try:
               self.gen.next()
           except StopIteration:
               return
           else:
               raise RuntimeError("generator didn't stop")
       else:
           try:
               self.gen.throw(type, value, traceback)
               raise RuntimeError("generator didn't stop after throw()")
           except StopIteration:
               return True
           except:
               # only re-raise if it's *not* the exception that was
               # passed to throw(), because __exit__() must not raise
               # an exception unless __exit__() itself failed.  But
               # throw() has to raise the exception to signal
               # propagation, so this fixes the impedance mismatch
               # between the throw() protocol and the __exit__()
               # protocol.
               #
               if sys.exc_info()[1] is not value:
                   raise

def contextmanager(func):
   def helper(*args, **kwds):
       return GeneratorContextManager(func(*args, **kwds))
   return helper
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Peter(阿斯拉达)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值