DeepOps的Python小笔记-python的with关键字的小例子

官方文档中的with关键字介绍:
https://www.python.org/dev/peps/pep-0343/

其他参考文档:
https://developer.ibm.com/zh/articles/os-cn-pythonwith/
http://blog.kissdata.com/2014/05/23/python-with.html

示例:

1 使用锁的时候使用,代码块执行完时自动释放

A template for ensuring that a lock, acquired at the start of a block, is released when the block is left:

@contextmanager
def locked(lock):
    lock.acquire()
    try:
        yield
    finally:
        lock.release()

Used as follows:

with locked(myLock):
    # Code here executes with myLock held.  The lock is
    # guaranteed to be released when the block is left (even
    # if via return or by an uncaught exception).

2 打开文件的时候使用,代码块执行完时自动关闭文件

A template for opening a file that ensures the file is closed when the block is left:

@contextmanager
def opened(filename, mode="r"):
    f = open(filename, mode)
    try:
        yield f
    finally:
        f.close()

Used as follows:

with opened("/etc/passwd") as f:
    for line in f:
        print line.rstrip()

3 事务处理的时候使用,代码块执行完时自动提交或回滚

A template for committing or rolling back a database transaction:

@contextmanager
def transaction(db):
    db.begin()
    try:
        yield None
    except:
        db.rollback()
        raise
    else:
        db.commit()

4 Example 1 rewritten without a generator:

class locked:
   def __init__(self, lock):
       self.lock = lock
   def __enter__(self):
       self.lock.acquire()
   def __exit__(self, type, value, tb):
       self.lock.release()

(This example is easily modified to implement the other relatively stateless examples; it shows that it is easy to avoid the need for a generator if no special state needs to be preserved.)

5 Redirect stdout temporarily:

@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

Used as follows:

with opened(filename, "w") as f:
    with stdout_redirected(f):
        print "Hello world"

This isn’t thread-safe, of course, but neither is doing this same dance manually. In single-threaded programs (for example, in scripts) it is a popular way of doing things.

6 A variant on opened() that also returns an error condition:

@contextmanager
def opened_w_error(filename, mode="r"):
    try:
        f = open(filename, mode)
    except IOError, err:
        yield None, err
    else:
        try:
            yield f, None
        finally:
            f.close()

Used as follows:

with opened_w_error("/etc/passwd", "a") as (f, err):
    if err:
        print "IOError:", err
    else:
        f.write("guido::0:0::/:/bin/sh\n")

7 Another useful example would be an operation that blocks signals. The use could be like this:

import signal

with signal.blocked():
    # code executed without worrying about signals

An optional argument might be a list of signals to be blocked; by default all signals are blocked. The implementation is left as an exercise to the reader.

8 Another use for this feature is the Decimal context. Here’s a simple example, after one posted by Michael Chermside:

import decimal

@contextmanager
def extra_precision(places=2):
    c = decimal.getcontext()
    saved_prec = c.prec
    c.prec += places
    try:
        yield None
    finally:
        c.prec = saved_prec

Sample usage (adapted from the Python Library Reference):

def sin(x):
    "Return the sine of x as measured in radians."
    with extra_precision():
        i, lasts, s, fact, num, sign = 1, 0, x, 1, x, 1
        while s != lasts:
            lasts = s
            i += 2
            fact *= i * (i-1)
            num *= x * x
            sign *= -1
            s += num / fact * sign
    # The "+s" rounds back to the original precision,
    # so this must be outside the with-statement:
    return +s

9 Here’s a simple context manager for the decimal module:

@contextmanager
def localcontext(ctx=None):
    """Set a new local decimal context for the block"""
    # Default to using the current context
    if ctx is None:
        ctx = getcontext()
    # We set the thread context to a copy of this context
    # to ensure that changes within the block are kept
    # local to the block.
    newctx = ctx.copy()
    oldctx = decimal.getcontext()
    decimal.setcontext(newctx)
    try:
        yield newctx
    finally:
        # Always restore the original context
        decimal.setcontext(oldctx)

Sample usage:

from decimal import localcontext, ExtendedContext

def sin(x):
    with localcontext() as ctx:
        ctx.prec += 2
        # Rest of sin calculation algorithm
        # uses a precision 2 greater than normal
    return +s # Convert result to normal precision

def sin(x):
    with localcontext(ExtendedContext):
        # Rest of sin calculation algorithm
        # uses the Extended Context from the
        # General Decimal Arithmetic Specification
    return +s # Convert result to normal context

10 A generic “object-closing” context manager:

class closing(object):
    def __init__(self, obj):
        self.obj = obj
    def __enter__(self):
        return self.obj
    def __exit__(self, *exc_info):
        try:
            close_it = self.obj.close
        except AttributeError:
            pass
        else:
            close_it()

This can be used to deterministically close anything with a close method, be it file, generator, or something else. It can even be used when the object isn’t guaranteed to require closing (e.g., a function that accepts an arbitrary iterable):

# emulate opening():
with closing(open("argument.txt")) as contradiction:
   for line in contradiction:
       print line

# deterministically finalize an iterator:
with closing(iter(data_source)) as data:
   for datum in data:
       process(datum)

(Python 2.5’s contextlib module contains a version of this context manager)

11 PEP 319 gives a use case for also having a released() context to temporarily release a previously acquired lock; this can be written very similarly to the locked context manager above by swapping the acquire() and release() calls:

class released:
  def __init__(self, lock):
      self.lock = lock
  def __enter__(self):
      self.lock.release()
  def __exit__(self, type, value, tb):
      self.lock.acquire()

Sample usage:

with my_lock:
    # Operations with the lock held
    with released(my_lock):
        # Operations without the lock
        # e.g. blocking I/O
    # Lock is held again here

12 A “nested” context manager that automatically nests the supplied contexts from left-to-right to avoid excessive indentation:

@contextmanager
def nested(*contexts):
    exits = []
    vars = []
    try:
        try:
            for context in contexts:
                exit = context.__exit__
                enter = context.__enter__
                vars.append(enter())
                exits.append(exit)
            yield vars
        except:
            exc = sys.exc_info()
        else:
            exc = (None, None, None)
    finally:
        while exits:
            exit = exits.pop()
            try:
                exit(*exc)
            except:
                exc = sys.exc_info()
            else:
                exc = (None, None, None)
        if exc != (None, None, None):
            # sys.exc_info() may have been
            # changed by one of the exit methods
            # so provide explicit exception info
            raise exc[0], exc[1], exc[2]

Sample usage:

with nested(a, b, c) as (x, y, z):
    # Perform operation

Is equivalent to:

with a as x:
    with b as y:
        with c as z:
            # Perform operation
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值