I would like to know what is the python way of initializing a class member but only when accessing it, if accessed. I tried the code below and it is working but is there something simpler than that?
class MyClass(object):
_MY_DATA = None
@staticmethod
def _retrieve_my_data():
my_data = ... # costly database call
return my_data
@classmethod
def get_my_data(cls):
if cls._MY_DATA is None:
cls._MY_DATA = MyClass._retrieve_my_data()
return cls._MY_DATA
You could use a @property
on the metaclass instead:
class MyMetaClass(type):
@property
def my_data(cls):
if getattr(cls, '_MY_DATA', None) is None:
my_data = ... # costly database call
cls._MY_DATA = my_data
return cls._MY_DATA
class MyClass(metaclass=MyMetaClass):
# ...
This makes my_data
an attribute on the class, so the expensive database call is postponed until you try to access MyClass.my_data
. The result of the database call is cached by storing it in MyClass._MY_DATA
, the call is only made once for the class.
For Python 2, use class MyClass(object):
and add a __metaclass__ = MyMetaClass
attribute in the class definition body to attach the metaclass.
Demo:
>>> class MyMetaClass(type):
... @property
... def my_data(cls):
... if getattr(cls, '_MY_DATA', None) is None:
... print("costly database call executing")
... my_data = 'bar'
... cls._MY_DATA = my_data
... return cls._MY_DATA
...
>>> class MyClass(metaclass=MyMetaClass):
... pass
...
>>> MyClass.my_data
costly database call executing
'bar'
>>> MyClass.my_data
'bar'
This works because a data descriptor like property
is looked up on the parent type of an object; for classes that's type
, and type
can be extended by using metaclasses.