2012-05-09 19 views
4

使用數據庫(或文件)時,我發現非常方便的python的一種功能是您可以爲類提供的__enter____exit__函數。現在,通過使用with語句,您可以確保該塊__enter__首次調用(你可以打開數據庫或文件),它的完成__exit__後調用(你可以關閉數據庫或文件。類中的所有函數是否存在__enter__和__exit__等價物?

我我想在每次調用我的Database類的函數時打開和關閉一個sqlite事務,我可以在每個函數的開始和結束時執行它,但是因爲每個函數都必須完成,所以我想知道,在每次函數調用之前和之後都有調用的方法嗎?像單元測試中的SetUp和TearDown一樣。

回答

7

你可以用一個餡餅裝飾器來裝飾每個成員函數,比如

@transaction 
def insertData(self): 
    # code 

和事務是一個裝飾器,您可以使用pre和post來定義函數。 是的,你必須爲每個功能做到這一點。這裏是一個例子

def transaction(f): 
    def pre(): 
     print "pre transaction" 
    def post(): 
     print "post transaction" 

    def wrapped(*args): 
     pre() 
     f(*args) 
     post() 

    return wrapped 


class Foo(object): 
    def __init__(self): 
     print "instantiating" 

    def doFoo(self): 
     print "doing foo" 

    @transaction 
    def doBar(self, value): 
     print "doing bar "+str(value) 

@transaction 
def foofunc(): 
    print "hello" 

foofunc() 

f=Foo() 
f.doFoo() 
f.doBar(5) 

stefanos-imac:python borini$ python decorator.py 
pre transaction 
hello 
post transaction 
instantiating 
doing foo 
pre transaction 
doing bar 5 
post transaction 

另一種方法就是使用一個元類,像這樣:

import types 


class DecoratedMetaClass(type): 
    def __new__(meta, classname, bases, classDict): 
     def pre(): 
      print "pre transaction" 
     def post(): 
      print "post transaction" 
     newClassDict={} 
     for attributeName, attribute in classDict.items(): 
      if type(attribute) == types.FunctionType: 
       def wrapFunc(f): 
        def wrapper(*args): 
         pre() 
         f(*args) 
         post() 
        return wrapper 
       newAttribute = wrapFunc(attribute) 
      else: 
       newAttribute = attribute 
      newClassDict[attributeName] = newAttribute 
     return type.__new__(meta, classname, bases, newClassDict) 



class MyClass(object): 

    __metaclass__ = DecoratedMetaClass 

    def __init__(self): 
     print "init" 
    def doBar(self, value): 
     print "doing bar "+str(value) 
    def doFoo(self): 
     print "doing foo" 



c = MyClass() 
c.doFoo() 
c.doBar(4) 

這是純粹的黑魔法,但它的工作原理

stefanos-imac:python borini$ python metaclass.py 
pre transaction 
init 
post transaction 
pre transaction 
doing foo 
post transaction 
pre transaction 
doing bar 4 
post transaction 

通常你不想裝飾__init__,並且您可能只想修飾具有特殊名稱的那些方法,因此您可能需要替換

 for attributeName, attribute in classDict.items(): 
      if type(attribute) == types.FunctionType: 

的東西,如

 for attributeName, attribute in classDict.items(): 
      if type(attribute) == types.FunctionType and "trans_" in attributeName[0:6]: 

這樣,只有調用的方法trans_whatever將transactioned。

+0

不要打擾切片像這樣的字符串; 'str.startswith()'更具可讀性。 – Kevin

相關問題