使用已缓存的属性在一个命名元组上。

huangapple go评论107阅读模式
英文:

Using a cached property on a named tuple

问题

  1. from typing import NamedTuple
  2. from functools import cached_property
  3. # 定义一个名为Rectangle的命名元组类
  4. class Rectangle(NamedTuple):
  5. x: int
  6. y: int
  7. # 使用cached_property装饰器定义一个计算面积的属性
  8. @cached_property
  9. def area(self):
  10. return self.x * self.y

我认为这个类定义会对Rectangle上的__slots__属性发出一些警告,但显然这个类定义是有效的。直到实际访问getter时才会失败:

  1. >>> rect = Rectangle(2, 3)
  2. >>> rect.area
  3. ...
  4. TypeError: Cannot use cached_property instance without calling __set_name__ on it.

嗯,这很奇怪,但没关系。

  1. >>> Rectangle.area.__set_name__(Rectangle, "area")
  2. >>> rect.area
  3. ...
  4. TypeError: No '__dict__' attribute on 'Rectangle' instance to cache 'area' property.

是否有更好的方法来在命名元组上使用缓存属性?要求如下:

  • 它不应该看起来像一个真正的字段(x, y, area = rect 不应该可行)
  • 它应该是延迟计算的(不是急切计算),并且缓存(不会每次访问都重新计算)
  • 存储位置不应该泄漏内存(应该在删除元组实例本身时删除)
  1. <details>
  2. <summary>英文:</summary>

from typing import NamedTuple
from functools import cached_property

class Rectangle(NamedTuple):
x: int
y: int

  1. @cached_property
  2. def area(self):
  3. return self.x * self.y
  1. I thought this class definition would complain something about the `__slots__` on Rectangle, but apparently the class definition is valid. It doesn&#39;t fail until too late, if/when the getter is actually accessed:

>>> rect = Rectangle(2, 3)
>>> rect.area
...
TypeError: Cannot use cached_property instance without calling set_name on it.
>>> Rectangle.

  1. Well, that&#39;s weird, but okay..

>>> Rectangle.area.set_name(Rectangle, "area")
>>> rect.area
...
TypeError: No 'dict' attribute on 'Rectangle' instance to cache 'area' property.

  1. Is there a better recipe for cached properties on named tuples? Requirements:
  2. - It should not appear to be a real field (`x, y, area = rect` should not be possible)
  3. - It should be lazy (not eagerly computed) and cached (not recomputed every time accessed)
  4. - Wherever the storage is should not leak memory (it should be deleted when the tuple instance itself is deleted)
  5. </details>
  6. # 答案1
  7. **得分**: 1
  8. 你可能想要在具有`frozen=True`设置的`dataclass`上使用`cached_property`。冻结设置允许`dataclass`的行为类似于不可变的`NamedTuple`
  9. ```python
  10. from dataclasses import dataclass
  11. from functools import cached_property
  12. @dataclass(frozen=True)
  13. class Rectangle:
  14. x: int
  15. y: int
  16. @cached_property
  17. def area(self):
  18. print("计算面积")
  19. return self.x * self.y
  20. r = Rectangle(2, 4)
  21. r.area
  22. 计算面积
  23. 8
  24. # 这是缓存的,所以第二次调用area时不会运行print
  25. r.area
  26. 8
  27. # 无法添加新属性
  28. r.z = 'thing'
  29. Traceback (most recent call last):
  30. File "<stdin>", line 1, in <module>
  31. File "<string>", line 4, in __setattr__
  32. dataclasses.FrozenInstanceError: 不能分配给字段 'z'
  33. # 无法重新分配现有属性
  34. r.x = 4
  35. Traceback (most recent call last):
  36. File "<stdin>", line 1, in <module>
  37. File "<string>", line 4, in __setattr__
  38. dataclasses.FrozenInstanceError: 不能分配给字段 'x'
  39. # 你会自动获得__str__
  40. print(r)
  41. Rectangle(x=2, y=4)
  42. # 以及__hash__
  43. hash(r)
  44. 3516302870623680066
  45. # 和__eq__
  46. r == Rectangle(2, 4)
  47. True
  48. r == Rectangle(1, 4)
  49. False
英文:

You probably want a cached_property on a dataclass with the frozen=True setting instead. The frozen setting allows the dataclass to function like an immutable NamedTuple:

  1. from dataclasses import dataclass
  2. from functools import cached_property
  3. @dataclass(frozen=True)
  4. class Rectangle:
  5. x: int
  6. y: int
  7. @cached_property
  8. def area(self):
  9. print(&quot;Fresh compute of area&quot;)
  10. return self.x * self.y
  11. r = Rectangle(2, 4)
  12. r.area
  13. Fresh compute of area
  14. 8
  15. # this is cached, so print doesn&#39;t run on a second
  16. # call to area
  17. r.area
  18. 8
  19. # can&#39;t add new attributes
  20. r.z = &#39;thing&#39;
  21. Traceback (most recent call last):
  22. File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
  23. File &quot;&lt;string&gt;&quot;, line 4, in __setattr__
  24. dataclasses.FrozenInstanceError: cannot assign to field &#39;z&#39;
  25. # can&#39;t reassign existing attributes
  26. r.x = 4
  27. Traceback (most recent call last):
  28. File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
  29. File &quot;&lt;string&gt;&quot;, line 4, in __setattr__
  30. dataclasses.FrozenInstanceError: cannot assign to field &#39;x&#39;
  31. # You get __str__ for free
  32. print(r)
  33. Rectangle(x=2, y=4)
  34. # as well as __hash__
  35. hash(r)
  36. 3516302870623680066
  37. # and __eq__
  38. r == Rectangle(2, 4)
  39. True
  40. r == Rectangle(1, 4)
  41. False
  42. </details>

huangapple
  • 本文由 发表于 2023年7月11日 04:02:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76656973.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定