延迟加载描述符

发布于 2024-12-01 13:31:12 字数 1479 浏览 2 评论 0原文

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

束缚m 2024-12-08 13:31:12

好吧,这里有两个问题:

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

因此,您应该创建一个具有实际值的属性,并检查它,而不是设置“已加载”标志。如果你不希望它是只读的,你应该实现__set__。否则,返回值并让 __get__ 处理赋值,而不是 load 中的 self.x = self.y

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)

Well, there are two problems here:

  1. You return None from __get__, whereas it should be the value you want x to represent.
  2. You do 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 of self.x = self.y in load, return the value and let __get__ to take care of the assignment.

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)
一个人的旅程 2024-12-08 13:31:12

您想要的可能更像是这样:

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

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

What you want is probably more like this:

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)

To make your code work you need a few 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)

Descriptors know on which object they are stored, but they don't know on which attribute. You want to intercept the access of a attribute, not change the returned value, so you want __getattr__ instead of descriptors.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文