Pythonic способ выполнять работу только при первом вызове переменной

В моем классе Python есть переменные, которые требуют работы для вычисления при первом вызове. Последующие вызовы должны просто возвращать предварительно вычисленное значение.

Я не хочу тратить время на выполнение этой работы, если они не нужны пользователю. Так есть ли чистый Pythonic способ реализовать этот вариант использования?

Сначала я хотел использовать property() для вызова функции в первый раз, а затем переопределить переменную:

class myclass(object):
    def get_age(self):
        self.age = 21 # raise an AttributeError here
        return self.age

    age = property(get_age)

Спасибо

5 ответов

Решение
class myclass(object):
    def __init__(self):
        self.__age=None
    @property
    def age(self):
        if self.__age is None:
            self.__age=21  #This can be a long computation
        return self.__age

Алекс упомянул, что вы можете использовать __getattr__, вот как это работает

class myclass(object):
    def __getattr__(self, attr):
        if attr=="age":
            self.age=21   #This can be a long computation
        return super(myclass, self).__getattribute__(attr)

__getattr__() вызывается, когда атрибут не существует на объекте, т.е. первый раз, когда вы пытаетесь получить доступ age, Каждый раз после age существует так __getattr__ не вызывается

property, как вы видели, не позволит вам переопределить это. Вам нужно использовать немного другой подход, такой как:

class myclass(object):

    @property
    def age(self):
      if not hasattr(self, '_age'):
        self._age = self._big_long_computation()
      return self._age

Есть и другие подходы, такие как __getattr__ или пользовательский класс дескриптора, но этот проще!-)

Вот декоратор из Python Cookbook для этой проблемы:

class CachedAttribute(object):
    ''' Computes attribute value and caches it in the instance. '''
    def __init__(self, method, name=None):
        # record the unbound-method and the name
        self.method = method
        self.name = name or method.__name__
    def __get__(self, inst, cls):
        if inst is None:
            # instance attribute accessed on class, return self
            return self
        # compute, cache and return the instance's attribute value
        result = self.method(inst)
        setattr(inst, self.name, result)
        return result

Да, вы можете использовать свойства, хотя ленивая оценка также часто выполняется с использованием дескрипторов, см., Например:

http://blog.pythonisito.com/2008/08/lazy-descriptors.html

Этому вопросу уже 11 лет, а python 3.8 и выше теперь поставляется с cached_property, который идеально подходит для этой цели. Свойство вычисляется только один раз, а затем сохраняется в памяти для последующего использования.

Вот как его использовать в этом случае:

      class myclass(object):
    @cached_property
    def age(self):
        return 21  #This can be a long computation
Другие вопросы по тегам