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
Да, вы можете использовать свойства, хотя ленивая оценка также часто выполняется с использованием дескрипторов, см., Например:
Этому вопросу уже 11 лет, а python 3.8 и выше теперь поставляется с cached_property, который идеально подходит для этой цели. Свойство вычисляется только один раз, а затем сохраняется в памяти для последующего использования.
Вот как его использовать в этом случае:
class myclass(object):
@cached_property
def age(self):
return 21 #This can be a long computation