类型暗示实例变量的数据类,该变量接受多种类型作为参数并存储单个类型

问题描述 投票:0回答:1

我希望我的类构造函数接受变量输入,然后将它们规范化以进行存储和访问。例如:

class Ticket:
    def __init__(self, number: int | str):
        self.number: int = int(number)

# so that it's flexible in creation:
t = Ticket(6)
t = Ticket('7')

# but consistent when accessed:
isinstance(t.number, int)  # True

我不知道正确的 OOP 术语,但我想确定我的类的接口?签名?正确反映它将接受

.number
作为 int 或 string,但访问
.number
将始终给出 int。

上面的方法有效(尽管我愿意接受建议),但是尝试对数据类执行相同的操作会在 Pylance 中出现类型错误:

@dataclass
class Ticket:
    number: int | str
   #^^^^^^ Pylance: Declaration "number" is obscured by a declaration of the same name

    def __post_init__(self):
        self.number: int = int(self.number)

这个问题在数据类版本中可以修复吗?或者只是数据类的限制?如果可能的话,我想保留数据类的其他好处。

python python-typing python-dataclasses
1个回答
0
投票

没有一种超级漂亮的方法可以做到这一点;同一事物的类级别注释和

__post_init__
级别注释是冲突的,因此以静态分析可接受的方式处理它的唯一方法就是使它们成为不同的事物。您可以通过以下方式执行此操作:

  1. 制作一个单独注释的字段,它是一个
    InitVar
    ,为初始化提供的东西,但这不是对象的属性
  2. 显式地设置属性字段
    init=False
    ,因此它接受初始化,但一个属性

在一起,你最终会得到这样的结果:

from dataclasses import dataclass, field, InitVar

@dataclass
class Ticket:
    number: int = field(init=False)  # The actual attribute; not accepted by generated __init__
                                     # as an argument, only an int
    numinit: InitVar[int | str]  # The argument, that's not an attribute, and can be int or str

    def __post_init__(self, numinit):  # InitVar fields are passed to __post_init__
        self.number = int(numinit)     # And we can use them to initialize the uninitialized 

这并不完美;名称不同(初始化参数也不能被命名为

number
,至少不是没有一些可怕的黑客行为,这比一开始就简单地编写自己的
__init__
要糟糕得多)。如果名称需要匹配,您可以自己写
__init__
。当然,
dataclass
不会为你做这件事很烦人,但你仍然会得到所有other生成的代码(例如
__repr__
__eq__
),所以对于像这样的简单情况来说并不可怕(它是比
InitVar
更简单,不过如果您有很多属性,并且只需要一个
InitVar
,那么
InitVar
看起来会更好)。

@dataclass
class Ticket:
    number: int

    def __init__(self, number: int | str):  # Accept as union type
        self.number = int(numinit)          # Convert to single type
© www.soinside.com 2019 - 2024. All rights reserved.