how can I narrow dataclass annotations (i.e., how can I update type hints after handling default None in post_init)?

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

how can I narrow dataclass annotations (i.e., how can I update type hints after handling default None in post_init)?

问题

以下是代码的中文翻译部分:

我有一个数据类它可以接受一个关键字值或者如果未指定值则可以从其他属性推断出一个值

import dataclasses

@dataclasses.dataclass
class RelatedValues:
    primary: float
    _: dataclasses.KW_ONLY
    secondary: float | None = None

def __post_init__(self):
    if self.secondary is None:
        self.secondary = self.primary

这段代码有效但它让我在`.secondary`的类型提示中被困扰即使在`__post_init__`之后`.secondary`*绝对不可能*`None`。

`__post_init__`中对`self.secondary`进行`cast`操作不起作用但这个方法起作用

NULL_FLOAT = float(int(uuid.uuid4())

@dataclasses.dataclass
class RelatedValues:
    primary: float
    _: dataclasses.KW_ONLY
    secondary: float = NULL_FLOAT

def __post_init__(self):
    if self.secondary == NULL_FLOAT:
        self.secondary = self.primary

但这种方法感觉不太符合Python的风格

以下方法也有效

@dataclasses.dataclass
class RelatedValues:
    primary: float
    _: dataclasses.KW_ONLY
    _secondary: float | None = None

def __post_init__(self):
    if self._secondary is None:
        self.secondary = self.primary
    else:
        self.secondary = self._secondary

或者这种方法也可以

@dataclasses.dataclass
class RelatedValues:
    primary: float
    _: dataclasses.KW_ONLY
    _secondary: float | None = None

@property
def secondary(self) -> float:
    if self._secondary is None:
        self.secondary = self.primary
    else:
        self.secondary = self._secondary

但后两种方法只是为了类型缩小而操纵我的kwargs这种做法有点不太对

我漏掉了什么
英文:

I have a dataclass that can take a keyword value, or, if no value is specified, infer a value from other attributes.

import dataclasses
@dataclasses.dataclass
class RelatedValues:
primary: float
_: dataclasses.KW_ONLY
secondary: float | None = None
def __post_init__(self):
if self.secondary is None:
self.secondary = self.primary

This code works, but it leaves me stuck with float | None as the type hint for .secondary even though .secondary cannot possibly be None after __post_init__.

cast-ing self.secondary in __post_init__ doesn't work. This does:

NULL_FLOAT = float(int(uuid.uuid4())
@dataclasses.dataclass
class RelatedValues:
primary: float
_: dataclasses.KW_ONLY
secondary: float = NULL_FLOAT
def __post_init__(self):
if self.secondary == NULL_FLOAT:
self.secondary = self.primary

But it feels distinctly non-Pythonic.

This also works:

@dataclasses.dataclass
class RelatedValues:
primary: float
_: dataclasses.KW_ONLY
_secondary: float | None = None
def __post_init__(self):
if self._secondary is None:
self.secondary = self.primary
else:
self.secondary = self._secondary

or this:

@dataclasses.dataclass
class RelatedValues:
primary: float
_: dataclasses.KW_ONLY
_secondary: float | None = None
@property
def secondary(self) -> float:
if self._secondary is None:
self.secondary = self.primary
else:
self.secondary = self._secondary

But the latter two are just mangling my kwargs for the sake of type narrowing, which kind of feels wrong.

What am I missing?

答案1

得分: 1

如果 secondary 总是被赋予一个非 None 的值,那么在一开始就不应该将其类型定义为 float | None。实际上,只有作为 __init__ 方法的 参数 传递进来的 secondary_ 可能是 None

from dataclasses import dataclass, field, InitVar

@dataclass
class RelatedValues:
    primary: float
    secondary: float = field(init=False)
    secondary_: InitVar[float|None] = None

    def __post_init__(self, secondary_):
        if secondary_ is None:
            secondary_ = self.primary
        self.secondary = secondary_

只有在你一开始不想提供自己的 __init__ 方法时才需要上述步骤。例如,

@dataclass(init=False)
class RelatedValues:
    primary: float
    secondary: float

    def __init__(self, primary: float, secondary: float = None):
        if secondary is None:
            secondary = primary
        self.primary = primary
        self.secondary = secondary
英文:

If secondary will always be assigned a non-None value, it should not be typed as float | None in the first place. It's only the argument to __init__, used to initialize self.secondary, that might be None.

from dataclasses import dataclass, field, InitVar
@dataclass
class RelatedValues:
primary: float
secondary: float = field(init=False)
secondary_: InitVar[float|None] = None
def __post_init__(self, secondary_):
if secondary_ is None:
secondary_ = self.primary
self.secondary = secondary_

These hoops are only necessary if you don't want to provide your own __init__ method in the first place. For example,

@dataclass(init=False)
class RelatedValues:
primary: float
secondary: float
def __init__(self, primary: float, secondary: float = None):
if secondary is None:
secondary = primary
self.primary = primary
self.secondary = secondary

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

发表评论

匿名网友

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

确定