Дескриптор для отложенной загрузки - PullRequest
2 голосов
/ 25 августа 2011

Я хотел реализовать ленивую загрузку переменных, но мне кажется, что я немного неправильно понимаю дескрипторы. Я хотел бы иметь объектные переменные, которые при первом доступе вызовут функцию 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)

Это нормально? Я не уверен, что я что-то ломаю.

Ответы [ 2 ]

1 голос
/ 25 августа 2011

Ну, здесь есть две проблемы:

  1. Вы возвращаете None из __get__, тогда как это должно быть значение, которое вы хотите x представлять.
  2. Вы делаете 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)
0 голосов
/ 25 августа 2011

То, что вы хотите, вероятно, больше похоже на это:

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__ вместо дескрипторов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...