python:属性字段是自动缓存的吗?

Guy*_*Guy 21 python properties

我的问题是解释器运行相同的以下两段代码:

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
Run Code Online (Sandbox Code Playgroud)

而且更简单:

class A(object):
  @property
  def x(self):
      return ... #some complicated action
Run Code Online (Sandbox Code Playgroud)

即,解释器是否足够智能缓存属性x

我的假设是x不会改变 - 发现它很难,但一旦你找到它就没有理由再找到它.

Sve*_*ach 25

不,每次进入酒店时都会调用吸气剂.


fab*_*ioM 17

不需要添加memoize装饰器:

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)
Run Code Online (Sandbox Code Playgroud)

  • 对于一个简单的python属性,memoize装饰器可能有些过分. (7认同)
  • 另请注意,同时,还有[`functools.lru_cache(maxsize = 128,typed = False)`](https://docs.python.org/3/library/functools.html#functools.lru_cache)装饰器(也是允许`maxsize = None`参数) (3认同)

unu*_*tbu 14

属性不会自动缓存其返回值.每次访问属性时都会调用getter(和setter).

但是,Denis Otkidach编写了一个精彩的缓存属性装饰器(在Python Cookbook中发布,第2版,最初也是在PSF许可下的ActiveState上),仅用于此目的:

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
Run Code Online (Sandbox Code Playgroud)

以下是一个展示其用途的示例:

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()
Run Code Online (Sandbox Code Playgroud)


小智 12

对于可能在 2020 年阅读本文的任何人funcutils,从 Python 3.8 开始,此功能现在作为标准库的一部分在模块中可用。

https://docs.python.org/dev/library/functools.html#functools.cached_property

需要注意的是,定义自己__dict__(或根本不定义)或使用的类__slots__可能无法按预期工作。例如,NamedTuple和元类。


Jef*_*man 11

Python 3.2以后版本提供了一个内置的装饰器,可用于创建LRU缓存:

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

或者,如果你正在使用Flask/Werkzeug,那就是@cached_property装饰者.

对于Django,试试吧 from django.utils.functional import cached_property


Ana*_*ich 5

我不得不查一下,因为我有同样的问题。

标准库中的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+ :(
Run Code Online (Sandbox Code Playgroud)

  • 你知道,你可以堆叠`@property`和`@functools.lru_cache(maxsize)`。自 Python 3.2 起,`@functools.lru_cache` 可用 (2认同)