Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/363.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中缓存类属性_Python_Memoization - Fatal编程技术网

在Python中缓存类属性

在Python中缓存类属性,python,memoization,Python,Memoization,我正在用python编写一个类,我有一个属性,它需要相对较长的时间来计算,所以我只想做一次。而且,类的每个实例都不需要它,因此我不想在\uuuuu init\uuuu中默认执行它 我不熟悉Python,但不熟悉编程。我可以想出一种很容易做到的方法,但我一次又一次地发现,用“Pythonic”的方法做某事通常比我用其他语言的经验想到的要简单得多 在Python中是否有一种“正确”的方法可以做到这一点?最简单的方法可能是编写一个方法(而不是使用一个属性)来包装属性(getter方法)。在第一次调用时

我正在用python编写一个类,我有一个属性,它需要相对较长的时间来计算,所以我只想做一次。而且,类的每个实例都不需要它,因此我不想在
\uuuuu init\uuuu
中默认执行它

我不熟悉Python,但不熟悉编程。我可以想出一种很容易做到的方法,但我一次又一次地发现,用“Pythonic”的方法做某事通常比我用其他语言的经验想到的要简单得多


在Python中是否有一种“正确”的方法可以做到这一点?

最简单的方法可能是编写一个方法(而不是使用一个属性)来包装属性(getter方法)。在第一次调用时,此方法计算、保存并返回值;之后,它只返回保存的值。

通常的方法是将属性设置为a,并在第一次计算时存储该值

import time

class Foo(object):
    def __init__(self):
        self._bar = None

    @property
    def bar(self):
        if self._bar is None:
            print "starting long calculation"
            time.sleep(5)
            self._bar = 2*2
            print "finished long caclulation"
        return self._bar

foo=Foo()
print "Accessing foo.bar"
print foo.bar
print "Accessing foo.bar"
print foo.bar

你可以试着研究一下备忘录。它的工作方式是,如果在函数中传递相同的参数,它将返回缓存的结果。您可以找到有关的更多信息


此外,根据代码的设置方式(您说并非所有实例都需要它),您可以尝试使用某种flyweight模式,或延迟加载。

我曾经按照gnibbler的建议这样做,但我最终厌倦了这些小小的管理步骤

class MemoizeTest:

      _cache = {}
      def __init__(self, a):
          if a in MemoizeTest._cache:
              self.a = MemoizeTest._cache[a]
          else:
              self.a = a**5000
              MemoizeTest._cache.update({a:self.a})
因此,我构建了自己的描述符:

class cached_property(object):
    """
    Descriptor (non-data) for building an attribute on-demand on first use.
    """
    def __init__(self, factory):
        """
        <factory> is called such: factory(instance) to build the attribute.
        """
        self._attr_name = factory.__name__
        self._factory = factory

    def __get__(self, instance, owner):
        # Build the attribute.
        attr = self._factory(instance)

        # Cache the value; hide ourselves.
        setattr(instance, self._attr_name, attr)

        return attr

Python≥ 3.8 并已被合并为

Python≥ 3.2<3.8

您应该同时使用和装饰器:

import functools
class MyClass:
    @property
    @functools.lru_cache()
    def foo(self):
        print("long calculation here")
        return 21 * 2
有更详细的示例,还提到了以前Python版本的后端口

Python<3.2

Python wiki有一个(麻省理工学院许可)可以这样使用:

import random
# the class containing the property must be a new-style class
class MyClass(object):
   # create property whose value is cached for ten minutes
   @cached_property(ttl=600)
   def randint(self):
       # will only be evaluated every 10 min. at maximum.
       return random.randint(0, 100)
或者其他答案中提到的任何符合您需求的实现。

或者上面提到的后端口。

对于Python2,而不是Python3,下面是我要做的。这是您所能获得的最有效的方法:

class X:
    @property
    def foo(self):
        r = 33
        self.foo = r
        return r
说明:基本上,我只是用计算值重载属性方法。因此,在您第一次访问属性(对于该实例)之后,
foo
不再是属性,而是成为实例属性。这种方法的优点是缓存命中尽可能便宜,因为
self.\uuu dict\uuu
被用作缓存,如果不使用属性,则不会产生实例开销

这种方法不适用于Python3。

Python3.8包含装饰器

将类的方法转换为计算其值的属性 一次,然后缓存为 例如。类似于
property()
,添加了缓存。有用的 对于以其他方式计算的实例的昂贵计算属性 实际上是不可变的

此示例直接来自文档:

从functools导入缓存的\u属性
类数据集:
def uuu init uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
self.\u data=编号的顺序
@缓存的不动产
def stdev(自我):
返回statistics.stdev(self.\u数据)
@缓存的不动产
def差异(自身):
返回统计数据。方差(自身数据)
限制是,具有要缓存的属性的对象必须具有一个可变映射的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>属性,排除具有
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>插槽的类,除非
\uuuuuuuuuuuuuuuuuu,
classproperty
cachedclassproperty
decorators

要缓存类属性,请执行以下操作:

from descriptors import cachedclassproperty

class MyClass:
    @cachedclassproperty
    def approx_pi(cls):
        return 22 / 7

大多数(如果不是所有的话)当前的答案都是关于缓存实例属性的。要缓存类属性,只需使用字典即可。这样可以确保每个类计算一次属性,而不是每个实例计算一次

mapping={}
A类:
定义初始化(自):
如果self.\uuuuu class.\uuuuu.\uuuuuu name.\uuuuuuu不在映射中:
打印(‘扩展计算’)
映射[self.\uuuuuuu类\uuuuuu.\uuuuuuuu名称]=self.\uuuuuuuuu类\uuuuuuuuuu名称__
self.cached=映射[self.\uuuuuuuuuuuuuuuuuuuuuuuuuuu类\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
举例来说

foo=A()
bar=A()
打印(foo.cached、bar.cached)
给予


lru_缓存也被后端口到python 2:-1
lru_缓存的默认大小为128,这可能会导致属性函数被调用两次。如果要使用
lru\u缓存(无)
所有实例将永久保持活动状态。@对于128种不同的参数配置,orlp lru\u缓存的默认大小为128。只有当生成的对象多于缓存大小时,这才是一个问题,因为这里唯一改变的参数是self。如果要生成这么多对象,就不应该使用无界缓存,因为它会迫使您将所有调用过该属性的对象无限期地保留在内存中,这可能会导致可怕的内存泄漏。无论如何,您最好使用一种将缓存存储在对象本身中的缓存方法,这样缓存就会被清除。
@property@functools.lru\u cache()
方法给了我一个
TypeError:unhable type
错误,可能是因为
self
不可散列。小心!在我看来,它似乎是
functools.lru\u cache
导致类的实例只要在缓存中就避免GC。更好的解决方案是Python 3.8.0中的
functools.cached_属性
!下面是它的工作原理:实例变量。在第一次访问属性时,没有实例属性,只有描述符类属性,因此执行描述符。但是,在其执行过程中,描述符使用缓存的值创建实例属性。这意味着当第二次访问该属性时,t
class X:
    @property
    def foo(self):
        r = 33
        self.foo = r
        return r
from descriptors import cachedclassproperty

class MyClass:
    @cachedclassproperty
    def approx_pi(cls):
        return 22 / 7
Expansive calculation
A A