Use a class decorator to implement late-initialization

江枫思渺然 提交于 2020-01-03 16:17:07

问题


I am using some classes which need to connect to databases. The connection is only really needed when performing a real action. I want to delay the connection phase until it is really needed. For that, I want to do something similar to this:

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

But I do not know how to define the connect decorator for the class.

I could of course do something like this:

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

But using decorators seems a more readable solution. Is it possible?


回答1:


Rather than trying to decorate the functions that require connections, use a property for getting the connection itself.

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

As for a straight decorator example, playing off F.J's answer:

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

You could also make prerequisite more robust by making it create descriptors so that it can behave correctly for functions and static methods as well as class and instance methods.




回答2:


I do like sr2222's approach of using a property for getting the connection, however here is an approach with decorators which may be useful or at least informative (use of functools.wraps() is optional):

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



回答3:


Similar to sr2222's solution, but calling it what it is: a cached_property.

The code is more compact, uses reusable building blocks, and in my opinion more readable.

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

The definition of cached_property is found here.



来源:https://stackoverflow.com/questions/15392143/use-a-class-decorator-to-implement-late-initialization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!