gpt4 book ai didi

python - 通过属性使用具有依赖属性的数据类

转载 作者:太空宇宙 更新时间:2023-11-04 04:03:31 28 4
gpt4 key购买 nike

我有一个类,例如Circle ,具有依赖属性,radiuscircumference .使用 dataclass 是有意义的这里是因为 __init__ 的样板文件, __eq__ , __repr__以及订购方法( __lt__ , ...)。

我选择一个属性依赖于另一个属性,例如周长是根据半径计算的。由于该类应该支持使用任一属性进行初始化(+ 将它们包含在 __repr__ 以及 dataclasses.asdict 中),我对两者都进行了注释:

from dataclasses import dataclass
import math


@dataclass
class Circle:
radius: float = None
circumference: float = None

@property
def circumference(self):
return 2 * math.pi * self.radius

@circumference.setter
def circumference(self, val):
if val is not type(self).circumference: # <-- awkward check
self.radius = val / (2 * math.pi)

这需要我为 if val is not type(self).circumference 添加有点笨拙的检查因为如果没有向 __init__ 提供任何值,这就是 setter 将收到的内容.

然后如果我想通过声明 frozen=True 使类可哈希我需要更改 self.radius = ...object.__setattr__(self, 'radius', ...)因为否则这将尝试分配给卡住实例的字段。

所以我的问题是,这是否是将数据类与属性一起使用的合理方式,或者是否存在潜在(非显而易见的)障碍,我应该避免在这种情况下使用数据类?或者也许有更好的方法来实现这个目标?

最佳答案

对于初学者,您可以在 __init__ 方法中设置属性,如下所示:

from dataclasses import dataclass, InitVar
import math


@dataclass(frozen=True, order=True)
class CircleWithFrozenDataclass:
radius: float = 0
circumference: float = 0

def __init__(self, radius=0, circumference=0):
super().__init__()
if circumference:
object.__setattr__(self, 'circumference', circumference)
object.__setattr__(self, 'radius', circumference / (2 * math.pi))
if radius:
object.__setattr__(self, 'radius', radius)
object.__setattr__(self, 'circumference', 2 * math.pi * radius)

这仍将为您提供所有有用的 __eq____repr____hash__ 和排序方法注入(inject)。虽然 object.__setattr__ 看起来很丑,但请注意 CPython implementation itself uses object.__setattr__在为卡住的 dataclass 注入(inject)生成的 __init__ 方法时设置属性。

如果你真的想摆脱object.__setattr__,你可以设置frozen=False(默认值)并覆盖__setattr__方法自己。这是复制数据类的 frozen 特性是 implemented in CPython 的方式.请注意,您还必须打开 unsafe_hash=True,因为自 frozen=False 以来,不再注入(inject) __hash__

@dataclass(unsafe_hash=True, order=True)
class CircleUsingDataclass:
radius: float = 0
circumference: float = 0

_initialized: InitVar[bool] = False

def __init__(self, radius=0, circumference=0):
super().__init__()
if circumference:
self.circumference = circumference
self.radius = circumference / (2 * math.pi)
if radius:
self.radius = radius
self.circumference = 2 * math.pi * radius
self._initialized = True

def __setattr__(self, name, value):
if self._initialized and \
(type(self) is __class__ or name in ['radius', 'circumference']):
raise AttributeError(f"cannot assign to field {name!r}")
super().__setattr__(name, value)

def __delattr__(self, name, value):
if self._initialized and \
(type(self) is __class__ or name in ['radius', 'circumference']):
raise AttributeError(f"cannot delete field {name!r}")
super().__delattr__(name, value)

在我看来,默认情况下,卡住应该只发生在 __init__ 之后,但现在我可能会使用第一种方法。

关于python - 通过属性使用具有依赖属性的数据类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57791679/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com