【问题标题】:Descriptor for lazy loading延迟加载描述符
【发布时间】:2011-08-25 17:10:28
【问题描述】:

我想实现变量的延迟加载,但我似乎对描述符有点误解。我想要对象变量,它在第一次访问时将调用 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)

这样好吗?我不确定我是否在破坏东西。

【问题讨论】:

    标签: python lazy-loading descriptor


    【解决方案1】:

    嗯,这里有两个问题:

    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)
    

    【讨论】:

    • 这让我想到了我在编辑中提到的装饰器。我想避免两次提及变量名,而且只有一个加载函数(通常从 SQL 和其他来源获取东西,我只是不想过早或不必要地连接)
    【解决方案2】:

    你想要的可能更像这样:

    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)
    

    要使您的代码正常工作,您需要几个returns:

    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__ 而不是描述符。

    【讨论】:

    • 实际上我不想要返回值,因为我的 load() 函数没有返回值(见 EDIT)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-02
    • 1970-01-01
    • 2014-03-06
    • 2021-07-13
    • 2010-11-26
    • 2011-08-26
    相关资源
    最近更新 更多