context manager

来源:互联网 发布:用友nc系统java插件 编辑:程序博客网 时间:2024/06/05 08:54

1) The "with" statement
with expr [as VAR]:
    WITH-BLOCK

"with" statement is something like "RAII" in C++, when "with" statement finishes, all resources will be cleaned automatically even there is exception raised.

For e.g.
with open("c:/test.txt") as f:  # note this "f" is not returned by open() function, it is actually returned by f.__enter__() function
    for line in f:
      print line
# "f" is closed automatically

2) Under the hood:
  • The expression is evaluated and should result in an object called a “context manager”. The context manager must have __enter__() and __exit__() methods.
  • The context manager’s __enter__() method is called. The value returned from __enter__() is assigned to VAR. If no 'as VAR' clause is present, the value is simply discarded.
  • The code in WITH-BLOCK is executed.
  • If WITH-BLOCK raises an exception, the __exit__(type, value, traceback) is called with the exception details, the same values returned by sys.exc_info(). The __exit__() method’s return value controls whether the exception is re-raised:any false value re-raises the exception, and True will result in suppressing it. You’ll only rarely want to suppress the exception, because if you do the author of the code containing the ‘with‘ statement will never realize anything went wrong.
  • If WITH-BLOCK didn’t raise an exception, the __exit__() method is still called, but type, value, and traceback are all None.
3) Writing Context Manager (example)
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"
    def __enter__ (self):
        # Code to start a new transaction
        cursor = self.cursor()
        return cursor
    def __exit__ (self, type, value, tb): # when reaching the end of this func, "None" is returned implicitly.
        if tb is None:
            # No exception, so commit
            self.commit()
        else:
            # Exception occurred, so rollback.
            self.rollback()
            # return False
# Usage:
db_connection = DatabaseConnection()
with db_connection as cursor:
    cursor.execute('insert into ...')
    cursor.execute('delete from ...')
    # ... more operations ...

4) The "contextlib" module
The "contextlib" module provides some functions and a decorator that are useful for writing objects for use with the ‘with‘ statement. The decorator is called contextmanager(), and lets you write a single generator function instead of defining a new class.The generator should yield exactly one value.The code up to the yield will be executed as the __enter__() method, and the value yielded will be the method’s return value that will get bound to the variable in the ‘with‘ statement’s as clause, if any.The code after the yield will be executed in the __exit__() method. Any exception raised in the block will be raised by the yield statement.
Example:
[Rewrite the example above by using contextlib]
from contextlib import contextmanager
@contextmanager
def db_transaction (connection):
    cursor = connection.cursor()
    try:
        yield cursor
    except:
        connection.rollback()
        raise
    else:
        connection.commit()
# Usage:
db = DatabaseConnection()
with db_transaction(db) as cursor:
    ...

5) "contextlib.nested(mgr1, mgr2, ...)"
It combines a number of context managers so you don’t need to write nested ‘with‘ statements. 
In this example, the single ‘with‘ statement both starts a database transaction and acquires a thread lock:
lock = threading.Lock()
with nested (db_transaction(db), lock) as (cursor, locked):
    ...

6) "contextlib.closing(object)"
function returns object so that it can be bound to a variable, and calls object.close() at the end of the block.

Example:
import urllib, sys
from contextlib import closing
with closing(urllib.urlopen('http://www.yahoo.com')) as f:
    for line in f:
        sys.stdout.write(line)



0 0
原创粉丝点击