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