I wanted to implement lazy loading of variables, but I seem to misunderstand descriptors a bit. I’d like to have object variables, which on first access will call the obj.load() function which will initialize the variables with their real value. I wrote
class ToLoad(object):
def __init__(self, loader_name="load")
self.loader_name=loader_name
def __get__(self, obj, type):
if not (hasattr(obj, "_loaded") and obj._loaded):
obj._loaded=True
getattr(obj, self.loader_name)()
return None
class Test(object):
x=ToLoad()
def __init__(self, y):
self.y=y
def load(self):
print("Loading {}".format(self.y))
self.x=self.y
t1=Test(1)
t2=Test(2)
print("A", t1.x)
print("B", t2.x)
print("C", t1.x)
which fails to return the actual value on loading at least the first time. Could someone suggest another way to solve that problem? I’m not sure how to return the correct value in get since at that point I do not know that the attribute is called “x”? Any other way?
DAMN, can’t answer my own question… So here is the
EDIT:
Thanks for the input! However, my load() function does not return the variable itself, since it loads many different variables. And I want to minimize the notation for using lazy loading. So I came up with a decorator
class to_load:
def __init__(self, *vars, loader="load"):
self.vars=vars
self.loader=loader
def __call__(self, cls):
def _getattr(obj, attr):
if attr in self.vars:
getattr(obj, self.loader)()
return getattr(obj, attr)
else:
raise AttributeError
cls.__getattr__=_getattr
return cls
@to_load("a", "b")
class Test:
def load(self):
print("Loading")
self.a=1
self.b=2
t=Test()
print("Starting")
print(t.a)
print(t.b)
#print(t.c)
Is that OK? I’m not sure if I’m breaking things.
Well, there are two problems here:
Nonefrom__get__, whereas it should be the value you wantxto represent.x = y, but your descriptor doesn’t implement__set__.So, instead of setting a “is loaded” flag, you should rather create an attribute with the actual value, and check for that. If you don’t want it to be read-only, you should implement
__set__. Otherwise, instead ofself.x = self.yinload, return the value and let__get__to take care of the assignment.