python: поля свойств кэшируются автоматически? - PullRequest
20 голосов
/ 21 июня 2011

Мой вопрос заключается в том, что следующие два фрагмента кода выполняются интерпретатором одинаково:

class A(object):
  def __init__(self):
     self.__x = None

  @property
  def x(self):
     if not self.__x:
        self.__x = ... #some complicated action
     return self.__x

и намного проще:

class A(object):
  @property
  def x(self):
      return ... #some complicated action

Т.е. достаточно ли умен интерпретатор для кэширования свойства x?

Мое предположение состоит в том, что x не меняется - найти его трудно , но как только вы найдете его один раз, нет причин искать его снова.

Ответы [ 7 ]

24 голосов
/ 21 июня 2011

Нет, получатель будет вызываться каждый раз, когда вы получаете доступ к свойству.

17 голосов
/ 21 июня 2011

Нет необходимости добавлять памятку декоратор:

class memoized(object):
   """Decorator that caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned, and
   not re-evaluated.
   """
   def __init__(self, func):
      self.func = func
      self.cache = {}
   def __call__(self, *args):
      try:
         return self.cache[args]
      except KeyError:
         value = self.func(*args)
         self.cache[args] = value
         return value
      except TypeError:
         # uncachable -- for instance, passing a list as an argument.
         # Better to not cache than to blow up entirely.
         return self.func(*args)
   def __repr__(self):
      """Return the function's docstring."""
      return self.func.__doc__
   def __get__(self, obj, objtype):
      """Support instance methods."""
      return functools.partial(self.__call__, obj)

@memoized
def fibonacci(n):
   "Return the nth fibonacci number."
   if n in (0, 1):
      return n
   return fibonacci(n-1) + fibonacci(n-2)

print fibonacci(12)
14 голосов
/ 21 июня 2011

Свойства не кешируют автоматически свои возвращаемые значения.Получатель (и установщики) предназначены для вызова при каждом обращении к свойству.

Однако Денис Откидач написал замечательный кешированный декоратор атрибутов (опубликован в Поваренная книга Python, 2-е издание , а также первоначально на ActiveState PSF-лицензия ) для этой цели:

class cache(object):    
    '''Computes attribute value and caches it in the instance.
    Python Cookbook (Denis Otkidach) https://stackoverflow.com/users/168352/denis-otkidach
    This decorator allows you to create a property which can be computed once and
    accessed many times. Sort of like memoization.

    '''
    def __init__(self, method, name=None):
        # record the unbound-method and the name
        self.method = method
        self.name = name or method.__name__
        self.__doc__ = method.__doc__
    def __get__(self, inst, cls):
        # self: <__main__.cache object at 0xb781340c>
        # inst: <__main__.Foo object at 0xb781348c>
        # cls: <class '__main__.Foo'>       
        if inst is None:
            # instance attribute accessed on class, return self
            # You get here if you write `Foo.bar`
            return self
        # compute, cache and return the instance's attribute value
        result = self.method(inst)
        # setattr redefines the instance's attribute so this doesn't get called again
        setattr(inst, self.name, result)
        return result

Вот пример, демонстрирующий ее использование:

def demo_cache():
    class Foo(object):
        @cache
        def bar(self):
            print 'Calculating self.bar'  
            return 42
    foo=Foo()
    print(foo.bar)
    # Calculating self.bar
    # 42
    print(foo.bar)    
    # 42
    foo.bar=1
    print(foo.bar)
    # 1
    print(Foo.bar)
    # __get__ called with inst = None
    # <__main__.cache object at 0xb7709b4c>

    # Deleting `foo.bar` from `foo.__dict__` re-exposes the property defined in `Foo`.
    # Thus, calling `foo.bar` again recalculates the value again.
    del foo.bar
    print(foo.bar)
    # Calculating self.bar
    # 42

demo_cache()
11 голосов
/ 03 декабря 2015

Python 3.2 и выше предлагает встроенный декоратор, который можно использовать для создания кэша LRU:

@functools.lru_cache(maxsize=128, typed=False)

В качестве альтернативы, если вы используетеFlask / Werkzeug, есть декоратор @cached_property.

Для Django, попробуйте from django.utils.functional import cached_property

2 голосов
/ 02 декабря 2018

Мне пришлось поискать это, так как у меня был тот же вопрос.

Пакет functools из стандартной библиотеки также получит декоратор cached_property. К сожалению, он доступен только из Python 3.8 (на момент публикации 3,8a0). Альтернатива ожиданию состоит в том, чтобы использовать пользовательский, такой как этот, как упомянуто 0xc0de ) или Django, на данный момент, затем переключиться позже:

from django.utils.functional import cached_property
# from functools import cached_property # Only 3.8+ :(
2 голосов
/ 19 ноября 2013

Декоратор от Дениса Откидача, упомянутый в ответе @ unutbu, был опубликован в кулинарной книге O'Reilly's Python Cookbook.К сожалению, O'Reilly не указывает никакой лицензии на примеры кода - просто как неофициальное разрешение на повторное использование кода.

Если вам нужен декоратор кэшированных свойств с либеральной лицензией, вы можете использовать Ken Seehof * @cached_property из ActiveState кодовые рецепты .Он явно опубликован под лицензией MIT .

def cached_property(f):
    """returns a cached property that is calculated by function f"""
    def get(self):
        try:
            return self._property_cache[f]
        except AttributeError:
            self._property_cache = {}
            x = self._property_cache[f] = f(self)
            return x
        except KeyError:
            x = self._property_cache[f] = f(self)
            return x

    return property(get)
1 голос
/ 12 июля 2018

Примечание: Добавление для полноты доступных опций.

Нет, property по умолчанию не кэшируется.Однако есть несколько вариантов, чтобы получить такое поведение, я хотел бы добавить еще один к этому:

https://github.com/pydanny/cached-property

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