问题
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