我的问题是解释器运行相同的以下两段代码:
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
没有改变 - 发现它是硬,但一旦找到它就没有理由再找到它。
答案 0 :(得分:22)
不,每次访问该属性时都会调用getter。
答案 1 :(得分: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)
答案 2 :(得分:13)
属性不会自动缓存其返回值。每次访问属性时都会调用getter(和setter)。
但是,Denis Otkidach编写了一个精彩的缓存属性装饰器(在the Python Cookbook, 2nd edition中发布,最初也在ActiveState下的PSF license上),仅用于此目的:
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()
答案 3 :(得分:10)
Python 3.2 onwards提供了一个内置的装饰器,可用于创建LRU缓存:
@functools.lru_cache(maxsize=128, typed=False)
或者,如果你正在使用Flask / Werkzeug,那就是@cached_property
装饰者。
对于Django,请尝试from django.utils.functional import cached_property
答案 4 :(得分:10)
对于任何可能在2020年阅读此书的人来说,此功能现已在funcutils
模块中作为Python 3.8的标准库的一部分提供。
https://docs.python.org/dev/library/functools.html#functools.cached_property
重要的是,定义自己的__dict__
(或根本不定义一个)或使用__slots__
的类可能无法正常工作。例如NamedTuple
和元类。
答案 5 :(得分:2)
@ unutbu的答案中提到的Denis Otkidach的装饰者发表在O'Reilly的Python Cookbook中。不幸的是,O'Reilly没有为代码示例指定任何许可 - 就像重用代码的非正式许可一样。
如果您需要具有自由许可的缓存属性装饰器,则可以使用Ken Seehof中的ActiveState code recipes @cached_property
。它明确发布在MIT license。
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)
答案 6 :(得分:2)
由于我有同样的问题,我不得不查一下。
标准库中的functools package也将获得cached_property装饰器。不幸的是,它仅在Python 3.8中可用(截止本文发布之时,它是3.8a0)。等待的另一种方法是暂时使用this one as mentioned by 0xc0de)或Django的自定义变量,然后再切换:
from django.utils.functional import cached_property
# from functools import cached_property # Only 3.8+ :(
答案 7 :(得分:1)
注意:为了使可用选项完整,进行添加。
否,property
默认不被缓存。但是,有几种方法可以实现该功能,我想再添加一个: