Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.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_Oop_Memoization - Fatal编程技术网

Python 在对象中存储计算值

Python 在对象中存储计算值,python,oop,memoization,Python,Oop,Memoization,最近我写了一堆代码,如下所示: class A: def __init__(self, x): self.x = x self._y = None def y(self): if self._y is None: self._y = big_scary_function(self.x) return self._y def z(self, i): return nice_easy_function(self.y(), i)

最近我写了一堆代码,如下所示:

class A:
  def __init__(self, x):
    self.x = x
    self._y = None

  def y(self):
    if self._y is None:
      self._y = big_scary_function(self.x)

    return self._y

  def z(self, i):
    return nice_easy_function(self.y(), i)
在一个给定的类中,我可能有很多东西像这样工作
y
,我也可能有其他东西使用存储的预先计算的值。这是做事情的最佳方式还是你会推荐不同的方式

请注意,我在这里不进行预计算,因为您可能会使用
A
的实例,而不使用
y


我已经用Python编写了示例代码,但是如果相关的话,我会对其他语言的特定答案感兴趣。相反,我想听听Pythonistas关于他们是否觉得这段代码是Pythonic的意见。

作为一个自称的Pythonista,我更喜欢在这种情况下使用
属性
装饰器:

class A:
    def __init__(self, x):
        self.x = x

    @property
    def y(self):
        if not hasattr(self, '_y'):
            self._y = big_scary_function(self.x)
        return self._y

    def z(self, i):
        return nice_easy_function(self.y, i)
这里也计算了
self.\y
属性允许您在相同的基础上引用
self.x
self.y
。也就是说,在处理类的实例时,您将
x
y
都视为属性,即使
y
是作为方法编写的


我还使用了
not hasattr(self,''u y')
而不是
self。'u y是None
,这允许我跳过
中的
self.y=None
声明。当然,您可以在这里使用您的方法,并且仍然使用
属性
装饰器。

第一件事:这是Python中非常常见的模式(Django IIRC中甚至有一个
缓存的_属性
描述符类)

尽管如此,这里至少存在两个潜在问题

第一个是所有“缓存属性”实现所共有的,这是一个事实,即通常不希望属性访问触发一些繁重的计算。这是否真的是一个问题取决于上下文(以及读者近乎宗教的观点…)

第二个问题——更具体到您的示例中——是传统的缓存失效/状态一致性问题:这里有
y
作为
x
的函数,或者至少这是人们所期望的——但是重新绑定
x
不会相应地更新
y
。在这种情况下,通过使
x
也成为一个属性并使setter上的
\u y
无效,可以很容易地解决这个问题,但是接下来会发生更多意想不到的繁重计算

在这种情况下(取决于上下文和计算成本),我可能会保留记忆(带有无效),但提供一个更明确的getter来说明我们可能会进行一些计算

编辑:我误读了你的代码,想象在
y
-上有一个属性装饰器,这显示了这种模式有多么普遍;)。但我的话仍然有意义,特别是当一位“自称的蟒蛇学家”给出了支持计算属性的答案时

编辑:如果您想要一个或多或少通用的“缓存无效的缓存属性”,这里有一个可能的实现(可能需要更多测试等):

我并不认为增加的认知开销是值得的——通常情况下,简单的内联实现使代码更容易理解和维护(并且不需要更多的LOC)-但如果一个包需要大量缓存属性和缓存失效,它仍然可能有用。

下面的代码片段描述了我的pythonista方法

我的类继承了
\u reset\u attributes
中的
with attributes
,并使用它使可怕的值无效

class WithAttributes:

    def _reset_attributes(self, attributes):
        assert isinstance(attributes,list)
        for attribute in attributes:
            try:
                delattr(self, '_' + attribute)
            except:
                pass

class Square(WithAttributes):

    def __init__(self, size):
        self._size = size

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

    @property
    def size(self):
        return self._size

    @size.setter
    def size(self, size):
        self._size = size
        self._reset_attributes('area')

我不认为这有什么错,只要你对惰性地评估变量没问题。如果要渲染图形或类似的内容,可能需要提前计算。有一种替代方法使用
\uuuuu getattr\uuuu
。这里简要地展示了:
\uuuuu getattr\uuuu
是最好避免的,现在我们有了描述符…@brunodesshuilliers,我不知道如何使用描述符而不是
\uuuu getattr\uuuu
来定义使用
\uu slots\uu
的类中的“惰性计算”属性。谢谢。我认为这也将有助于我的IDE推断出y的错误类型。NB不想在这里发动圣战,我也提到我也使用这个模式,但是你必须考虑上下文和权重的利弊。你不需要基类和继承这里,一个简单的函数将工作相同。一个简单的例外条款刚刚通过,天哪!如果重点是提供一个通用的“带失效的缓存属性”,那么有更好的方法。@brunodesshuilliers您可以继续评论,但不要给出示例。如果你能给我们看,我们都会很高兴向你学习的。这显然是一个小片段,并没有显示所有内容。我留下了我认为会有帮助的东西。很抱歉,我在手机上发帖,所以键入完整的代码片段将是一件非常麻烦的事。StackOverflow是帮助需要帮助的人的好工具。我看不出你在这里出现,批评别人的答案有什么帮助。如果你不能帮我们打电话,那就别帮我们,让我们一个人呆着。我注意到您的大多数活动都是批评,而不是帮助编写代码示例。cf我编辑的答案。请随意批评,我不会生气的。谢谢。在我的用例中,
x
在实例初始化后永远不会改变,所以我不必担心失效。有鉴于此,还有什么我应该做的吗?如果
x
在初始化后不应该更改,您可能希望将其设置为只读属性,如果您选择坚持使用
y
的显式getter,那么将其命名为
get_y()
可能会避免一些混淆(只是将其命名为
y()
某种程度上使它看起来像一个属性,因此有人可能会忘记实际调用该方法,并对得到一个未绑定的方法作为值感到有点惊讶)。
class WithAttributes:

    def _reset_attributes(self, attributes):
        assert isinstance(attributes,list)
        for attribute in attributes:
            try:
                delattr(self, '_' + attribute)
            except:
                pass

class Square(WithAttributes):

    def __init__(self, size):
        self._size = size

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

    @property
    def size(self):
        return self._size

    @size.setter
    def size(self, size):
        self._size = size
        self._reset_attributes('area')