Skip to main content
 首页 » 编程设计

python之延迟加载的描述符

2025年05月04日12落叶无声

我想实现变量的延迟加载,但是我好像对描述符有点误解。我想要对象变量,在第一次访问时将调用 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”?还有其他办法吗?

该死,无法回答我自己的问题...所以这是 编辑: 感谢您的输入!但是,我的 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) 

可以吗?我不确定我是否在破坏东西。

请您参考如下方法:

嗯,这里有两个问题:

  1. 您从 __get__ 返回 None,而它应该是您希望 x 表示的值。
  2. 你做了 x = y,但你的描述符没有实现 __set__。

因此,与其设置“已加载”标志,不如创建一个具有实际值的属性,并检查它。如果你不希望它是只读的,你应该实现 __set__。否则,不是 load 中的 self.x = self.y,而是返回值并让 __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)