Python中的属性何时和多久计算一次?

8

可能是重复的问题:
python:属性字段是否自动缓存?

关于Python中属性的效率问题,我想知道它们被调用的时间和频率。

以一个简单的例子为例,假设我子类化了 namedtuple 并有以下内容:

from collections import namedtuple
from math import pi

class Circle (namedtuple('Circle', 'x, y, r')):
    __slots__ = ()

    @property
    def area(self):
        return pi*self.r**2

unitCircle = Circle(0, 0, 1.0)
print 'The area of the unit circle is {0} units'.format(unitCircle.area)

我假设该区域在第一次调用之前不会被计算,但一旦被调用,那个值会被缓存直到有变化或每次被调用时重新计算吗?
换句话说,如果我有一个属性(与此不同)相对昂贵,需要重复计算,并且将被多次使用,那么我应该让它成为一个属性,还是将其存储为一个值,并在真正需要更新时显式地计算它,这样更有效?

你不必二选一。你可以拥有一个属性,如果尚未加载,则会自动缓存,然后可以根据需要定期重新缓存。 - Silas Ray
2个回答

18

如果不显式地进行缓存,属性就不会被缓存,因此每次访问属性时都会运行代码。请尝试:

@property
def area(self):
    try:
        return self._area
    except AttributeError:
        area = pi*self.r**2
        self._area = area
        return area

如果您希望偶尔能够按需重新计算该值,请执行以下操作:

@property
def area(self):
    try:
        return self._area
    except AttributeError:
        self.recalc_area()
        return self._area

def recalc_area(self):
    self._area = pi*self.r**2

或者,如果你想要更自动化地完成它:

@property
def area(self):
    try:
        return self._area
    except AttributeError:
        area = pi*self.radius**2
        self._area = area
        return area

@property
def radius(self):
    return self._radius

@radius.setter
def radius(self, radius):
    try:
        del self._area
    except AttributeError:
        pass
    self._radius = radius

谢谢。所以,对于那些计算便宜或很少引用的值,像示例中的基本属性就可以了。但对于计算代价高且需要反复引用而不改变的值,使用更精细的缓存机制似乎比较明智。 - TimothyAWiseman
1
看起来是一个不错的分析。你甚至可以创建另一个装饰器,封装property以自动化缓存过程。我在网上看到了一些例子,甚至有一些库可以使依赖关系链接过程更加明确,例如区域>半径。 - Silas Ray

1
Python中的装饰器(如@property)在类加载时进行评估。有时,对类的影响将包括额外的函数,但装饰器本身只运行一次。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接