使用类装饰器实现延迟初始化

4 投票
3 回答
661 浏览
提问于 2025-04-17 18:58

我正在使用一些需要连接数据库的类。其实,只有在执行真正的操作时才需要连接数据库。我想把连接的过程延迟到真正需要的时候。为此,我想做一些类似下面的事情:

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()

不过,使用装饰器似乎是一个更易读的解决方案。这样做可以吗?

3 个回答

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的定义可以在这里找到。

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()
6

与其尝试给那些需要连接的函数加装饰,不如直接用一个属性来获取连接。

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()

至于一个简单的装饰器示例,可以参考F.J的回答:

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更强大一点,让它创建描述符,这样它就能正确地处理函数、静态方法、类方法和实例方法了。

撰写回答