Reputation: 221
I have a class where I need access to a computed value that can only be calculated per subclass. This computation is not cheap, and since there are many instantiations of the subclasses, I want to compute this value only once per subclass.
I can think of two solution which I don't really like:
Either the parent class will have a @classmethod
start()
which will compute the values.
this enforces me to identify the precise location of the first instantiation of each class, so I've ruled this option out.
or, this code:
class A(object):
@classmethod
def _set_cls_attribute(cls):
if hasattr(cls, 'big_attr'):
return
cls.big_attr = heavy_func(cls.VAL)
def __init__(self):
self._set_cls_attribute()
class B(A):
VAL = 'b'
class C(A):
VAL = 'c'
for _ in range(large_number):
b = B()
c = C()
I don't like using hasattr
though...
Is there anything better?
Upvotes: 1
Views: 205
Reputation: 304147
A metaclass is a handy way to solve this
class A_meta(type):
def __init__(cls, *args):
type.__init__(cls, *args)
if hasattr(cls, 'VAL'):
cls.big_attr = heavy_func(cls.VAL)
class A(object):
__metaclass__ = A_meta
class B(A):
VAL = 'b'
class C(A):
VAL = 'c'
Another way along the same lines as yours. This has the advantage of deferring the call to heavy_func until the attribute is first accessed.
class A(object):
def __getattr__(self, attr):
if attr == 'big_attr':
self.__class__.big_attr = heavy_func(self.VAL)
return object.__getattribute__(self, attr)
Upvotes: 1
Reputation: 1008
Without metaclasses or hasattr :
class A(object):
@classmethod
def attribute(cls):
v = heavy_func(cls.VAL)
cls.attribute = lambda k : v
return v
Upvotes: 1