2013-03-13 56 views
3

我正在使用一些需要連接到數據庫的類。只有在執行實際行動時才需要連接。我想延遲連接階段,直到真正需要它。對於這一點,我希望做一些與此類似:使用類裝飾器實現後期初始化

class MyClass 

    def __init__(self): 
     self.conn = None 

    def connect(self): 
     if self.conn : return 
     self.conn = ConnectToDatabase() 

    @connect 
    def do_something1(self): 
     self.conn.do_something1() 

    @connect 
    def do_something2(self): 
     self.conn.do_something2() 

但我不知道如何界定connect裝飾爲類。

我當然可以做這樣的事情:

def do_something1(self): 
     self.connect() 
     self.conn.do_something1() 

但使用的裝飾似乎是一種更可讀的解決方案。可能嗎?

回答

5

與其試圖裝飾需要連接的函數,不如使用屬性獲取連接本身。

class MyClass(object): 

    def __init__(self): 
     self._conn = None 

    @property 
    def conn(self): 
     if self._conn is None: 
      self._conn = ConnectToDatabase() 
     return self._conn 

    def do_something1(self): 
     self.conn.do_something1() 

    def do_something2(self): 
     self.conn.do_something2() 

至於直裝飾例如,挑撥離間FJ的回答是:

def prerequisite(prerequisite_function, *pre_args, **pre_kwargs): 
    def wrapper(func): 
     def wrapped(self, *args, **kwargs): 
      prerequisite_function(self, *pre_args, **pre_kwargs) 
      return func(self, *args, **kwargs) 
     return wrapped 
    return wrapper 

class MyClass(object): 

    def __init__(self): 
     self.conn = None 

    def connect(self): 
     if self.conn is None: 
      self.conn = ConnectToDatabase() 

    @prerequisite(connect) 
    def do_something(self): 
     self.conn.do_something() 

你也可以將prerequisite更強大的通過使創建描述符,以便它可以正確運行的功能和靜態方法以及類和實例方法。

+0

這太好了,謝謝。我何時會停止學習? – dangonfast 2013-03-13 17:29:23

+0

當你死亡。 :) – 2013-03-13 17:29:35

+0

如果你停止學習,你已經過時,將被更新的模型取代。 – kindall 2013-03-13 17:37:31

2

我不喜歡使用屬性獲得連接的sr2222的做法,但這裏是裝飾的方法可能是有用的,或者至少信息(使用functools.wraps()是可選的):

import functools 

def require_connection(f): 
    @functools.wraps(f) 
    def wrapped(self, *args, **kwargs): 
     self.connect() 
     return f(self, *args, **kwargs) 
    return wrapped 

class MyClass(object): 
    def __init__(self): 
     self.conn = None 

    def connect(self): 
     if self.conn : return 
     self.conn = ConnectToDatabase() 

    @require_connection 
    def do_something1(self): 
     self.conn.do_something1() 

    @require_connection 
    def do_something2(self): 
     self.conn.do_something2() 
+0

我還沒有嘗試過,但看起來很有趣。儘管如此,sr2222的答覆以我想要做的更簡單的方式進行,儘管這直接回復了我的問題,但我更喜歡其他解決方案。 – dangonfast 2013-03-13 17:42:14

+0

爲什麼要在課堂上做出這種方法呢?在課程創建完成後的任何時候,require_connection都將可用,但仍然會造成混淆無用/中斷。要麼使它成爲直接的幫助函數,要麼使其對元類更有意義。 – 2013-03-13 17:45:01

+0

@ sr2222好點,我把'require_connection'移出了課堂。 – 2013-03-13 17:55:05

2

類似sr2222的解決方案,但稱它是什麼:一個cached_property

代碼更緊湊,使用可重用的構建塊,在我看來更具可讀性。

class MyClass(object): 

    @cached_property 
    def conn(self): 
     return ConnectToDatabase() 

    def do_something1(self): 
     self.conn.do_something1() 

    def do_something2(self): 
     self.conn.do_something2() 

cached_property定義是發現here

+0

雖然'property'裝飾器是內置的,而'cached_property'不是。 – 2013-03-13 18:03:40

+0

正確。它仍然是一個非常有用的構建塊。抓住它一次,充分利用它。 – shx2 2013-03-13 18:05:14