Sas*_*lla 11 python python-3.x python-3.7 python-dataclasses
我正在尝试构建一个@dataclass定义架构但实际上并未使用给定成员实例化的模型。(基本上,我@dataclass为了其他目的劫持了方便的语法)。这几乎就是我想要的:
@dataclass(frozen=True, init=False)
class Tricky:
thing1: int
thing2: str
def __init__(self, thing3):
self.thing3 = thing3
Run Code Online (Sandbox Code Playgroud)
但是我FrozenInstanceError在__init__方法中得到了一个:
dataclasses.FrozenInstanceError: cannot assign to field 'thing3'
Run Code Online (Sandbox Code Playgroud)
我需要frozen=True(为了哈希)。有什么方法可以在__init__冻结上设置自定义属性@dataclass吗?
Shm*_* H. 12
问题是默认__init__实现object.__setattr__()与冻结类一起使用,并且通过提供您自己的实现,您也必须使用它,这会使您的代码非常笨拙:
@dataclass(frozen=True, init=False)
class Tricky:
thing1: int
thing2: str
def __init__(self, thing3):
object.__setattr__(self, "thing3", thing3)
Run Code Online (Sandbox Code Playgroud)
不幸的是,python 没有提供使用默认实现的方法,因此我们不能简单地执行以下操作:
@dataclass(frozen=True, init=False)
class Tricky:
thing1: int
thing2: str
def __init__(self, thing3, **kwargs):
self.__default_init__(DoSomething(thing3), **kwargs)
Run Code Online (Sandbox Code Playgroud)
但是,通过我们可以很容易地实现该行为:
def dataclass_with_default_init(_cls=None, *args, **kwargs):
def wrap(cls):
# Save the current __init__ and remove it so dataclass will
# create the default __init__.
user_init = getattr(cls, "__init__")
delattr(cls, "__init__")
# let dataclass process our class.
result = dataclass(cls, *args, **kwargs)
# Restore the user's __init__ save the default init to __default_init__.
setattr(result, "__default_init__", result.__init__)
setattr(result, "__init__", user_init)
# Just in case that dataclass will return a new instance,
# (currently, does not happen), restore cls's __init__.
if result is not cls:
setattr(cls, "__init__", user_init)
return result
# Support both dataclass_with_default_init() and dataclass_with_default_init
if _cls is None:
return wrap
else:
return wrap(_cls)
Run Code Online (Sandbox Code Playgroud)
进而
@dataclass_with_default_init(frozen=True)
class DataClass:
value: int
def __init__(self, value: str):
# error:
# self.value = int(value)
self.__default_init__(value=int(value))
Run Code Online (Sandbox Code Playgroud)
更新:我打开了这个错误,我希望在 3.9 之前实现它。
Tim*_*mmm 10
这是一个更简单的选项 - 只需添加一个静态make函数:
@dataclass(frozen=True)
class Tricky:
thing1: str
thing2: int
thing3: bool
@classmethod
def make(cls, whatever: str, you: bool, want: float):
return cls(whatever + "..", you * 4, want > 5)
x = Tricky.make("foo", false, 3)
Run Code Online (Sandbox Code Playgroud)
根据您的make方法的用途,遵循 Rust 的命名约定 - 可能是个好主意from_foo()。例如
@dataclass(frozen=True)
class Coord:
lat: float
lon: float
@classmethod
def from_os_grid_reference(cls, x: int, y: int):
return cls(...)
@classmethod
def from_gps_nema_string(cls, nema_string: str):
return cls(...)
Run Code Online (Sandbox Code Playgroud)
我需要
frozen=True(为了哈希)。
没有严格的需要冻结一个类只是为了可散列。您可以选择不改变代码中任何位置的属性,unsafe_hash=True而是进行设置。
但是,您应该真正声明thing3为字段,而不是使用自定义__init__:
from dataclasses import dataclass, field
from typing import Any
@dataclass(unsafe_hash=True)
class Tricky:
thing1: int = field(init=False)
thing2: str = field(init=False)
thing3: Any
def __post_init__(self):
self.thing1 = 42
self.thing2 = 'foo'
Run Code Online (Sandbox Code Playgroud)
此处thing1并thing2已init=False设置,因此不会将它们传递给__init__方法。然后在__post_init__()方法中设置它们。
请注意,这现在要求您不要冻结该类,否则您无法设置thing1and thing2,不在自定义中__init__也不在__post_init__.
演示:
>>> Tricky('bar')
Tricky(thing1=42, thing2='foo', thing3='bar')
>>> hash(Tricky('bar'))
-3702476386127038381
Run Code Online (Sandbox Code Playgroud)
如果您想要的只是模式定义,则根本不需要数据类。您可以从任何类中获取类注释;无论是作为原始注释还是使用typing.get_type_hints().
事实证明,数据类没有提供您正在寻找的功能。然而Attrs确实:
from attr import attrs, attrib
@attrs(frozen=True)
class Name:
name: str = attrib(converter=str.lower)
Run Code Online (Sandbox Code Playgroud)
类似问题的相同答案:参见/sf/answers/4528692521/
| 归档时间: |
|
| 查看次数: |
7866 次 |
| 最近记录: |