我想实现延迟加载变量,但我似乎误解了描述符。我想要有对象变量,在第一次访问时会调用obj.load()函数,该函数将使用它们的实际值初始化变量。我写了
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)
至少第一次无法在加载时返回实际值。有人可以提出另一种方法来解决这个问题吗?我不确定如何在 get 中返回正确的值,因为此时我不知道该属性被称为“x”?还有别的吗?
DAMN,无法回答我自己的问题......所以这就是了 编辑: 感谢您的投入!但是,我的load()函数本身并不返回变量,因为它会加载许多不同的变量。我想最小化使用延迟加载的表示法。所以我想出了一个装饰师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)
可以吗?我不确定我是不是在破坏东西。
答案 0 :(得分:1)
嗯,这里有两个问题:
None
返回__get__
,而它应该是您希望x
代表的值。x = y
,但您的描述符未实现__set__
。因此,您应该创建一个具有实际值的属性,而不是设置“已加载”标志,并检查该属性。如果您不希望它是只读的,则应实现__set__
。否则,不是self.x = self.y
中的load
,而是返回值并让__get__
来处理作业。
class ToLoad(object):
def __init__(self, var, func):
self.var = var
self.func = func
# style note: try to avoid overshadowing built-ins (e.g. type)
def __get__(self, obj, cls):
try:
return getattr(obj, self.var)
except AttributeError:
value = getattr(obj, self.func)()
setattr(obj, self.var, value)
return value
class Foo(object):
x = ToLoad('x', '_load_x')
def __init__(self, y):
self.y = y
def _load_x(self):
print('Loading {0} into x'.format(self.y))
return self.y
a = Foo(1)
b = Foo(2)
print(a.x)
print(b.x)
print(a.x)
答案 1 :(得分:0)
你想要的可能更像是这样:
class Test(object):
def __init__(self, y):
self.y=y
def __getattr__(self, attr):
return self.load(attr)
def load(self, attr):
print("Loading `{}`".format(attr)) # ie "Loading `x`"
# get the value for `attr` somewhere, here always self.y
val = self.y
# store it on this object to avoid reloading it
setattr(self, attr, val)
return val
t1=Test(1)
t2=Test(2)
print("A", t1.x)
print("B", t2.x)
print("C", t1.x)
要使代码正常工作,您需要一些return
s:
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
return 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
return self.x
t1=Test(1)
t2=Test(2)
print("A", t1.x)
print("B", t2.x)
print("C", t1.x)
描述符知道它们存储在哪个对象上,但是它们不知道哪个属性。您希望拦截属性的访问权限,而不是更改返回的值,因此您需要__getattr__
而不是描述符。