1. 程式人生 > >contextmanager: 上下文管理器類和上下文管理器裝飾器

contextmanager: 上下文管理器類和上下文管理器裝飾器

原文

上下文管理器是在Python2.5之後加入的功能,可以在方便的需要的時候比較精確地分配和釋放資源, with便是上下文管理器的最廣泛的應用, 比如:

with open("test/test.txt","w") as f:
  f.write("hello")

這上會比使用try:...finally:f.close方便的多.

class MyResource:
    # __enter__ 返回的物件會被with語句中as後的變數接受
    def __enter__(self):
        print('connect to resource'
) return self def __exit__(self, exc_type, exc_value, tb): print('close resource conection') def query(self): print('query data')

類中有兩個特殊的魔術方法:
__enter__: with語句中的程式碼塊執行前, 會執行__enter__, 返回的值將賦值給with句中as後的變數.
__exit__: with語句中的程式碼塊執行結束或出錯, 會執行_exit__

比如以下程式碼:

with
Myresource() as r: r.query()

的列印結果為:

connect to resource
query data
close resource conection

那麼有沒有一個簡化定義的方法呢, python提供了一個裝飾器contextmanager

from contextlib import contextmanager
class MyResource:
    def query(self):
        print('query data')

@contextmanager
def make_myresource()
:
print('start to connect') yield MyResource() print('end connect') pass

被裝飾器裝飾的函式分為三部分:
with語句中的程式碼塊執行前執行函式中yield之前程式碼
yield返回的內容複製給as之後的變數
with程式碼塊執行完畢後執行函式中yield之後的程式碼

比如下方程式碼:

with make_myresource() as r:
     r.query()

的結果為:


start to connect
query data
end connect

在程式設計中如果頻繁的修改資料庫, 一味的使用類似try:... except..: rollback() raise e其實是不太好的.

比如某一段的程式碼的是這樣的:

   try:
        gift = Gift()
        gift.isbn = isbn
        ... 
        db.session.add(gift)
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        raise e

為了達到使用with語句的目的, 我們可以重寫db所屬的類:

from flask_sqlalchemy import SQLAlchemy as _SQLALchemy
class SQLAlchemy(_SQLALchemy):
    @contextmanager
    def auto_commit(self):
        try:
            yield
            self.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e

這時候, 在執行資料的修改的時候便可以:

 with db.auto_commit():
        gift = Gift()
        gift.isbn = isbndb.session.add(gift)
        db.session.add(gift)

with db.auto_commit():
    user = User()
    user.set_attrs(form.data)
    db.session.add(user)