【发布时间】:2022-09-27 16:09:15
【问题描述】:
假设我有一个自定义用例,我需要为数据类动态创建或定义__init__ 方法。
例如,假设我需要像@dataclass(init=False) 一样装饰它,然后修改__init__() 方法以获取关键字参数,例如**kwargs。但是,在kwargs 对象中,我只检查已知数据类字段的存在,并相应地设置这些属性(示例如下)
我想向我的 IDE (PyCharm) 输入提示,修改后的 __init__ 只接受列出的数据类字段作为参数或关键字参数。我不确定是否有办法解决这个问题,使用 typing 库或其他方式。我知道 PY3.11 有 dataclass transforms 计划,这可能会也可能不会做我想要的(我的直觉是不)。
这是我正在玩的示例代码,这是一个基本案例,说明了我遇到的问题:
from dataclasses import dataclass
# get value from input source (can be a file or anything else)
def get_value_from_src(_name: str, tp: type):
return tp() # dummy value
@dataclass
class MyClass:
foo: str
apple: int
def __init__(self, **kwargs):
for name, tp in self.__annotations__.items():
if name in kwargs:
value = kwargs[name]
else:
# here is where I would normally have the logic
# to read the value from another input source
value = get_value_from_src(name, tp)
if value is None:
raise ValueError
setattr(self, name, value)
c = MyClass(apple=None)
print(c)
c = MyClass(foo=\'bar\', # here, I would like to auto-complete the name
# when I start typing `apple`
)
print(c)
如果我们假设字段的数量或名称不固定,我很好奇是否有一种通用方法基本上可以对类型检查器说,\"这个类的__init__ 只接受与数据类本身定义的字段匹配的(可选)关键字参数\".
附录,基于以下 cmets 中的注释:
-
传递
@dataclass(kw_only=True)将不起作用,因为想象我正在为一个库编写这个,并且需要支持 Python 3.7+。此外,在实现自定义__init__()时,kw_only无效,如本例所示。 -
以上只是一个存根
__init__方法。它可能有更复杂的逻辑,例如基于文件源设置属性。基本上以上只是一个更大用例的示例实现。 -
我无法将每个字段更新为
foo: Optional[str] = None,因为该部分将在用户代码中实现,我无法控制。此外,当您知道将为您生成自定义__init__()方法时,以这种方式对其进行注释是没有意义的 - 这意味着不是由dataclasses生成的。最后,设置一个默认值每个字段,以便班级可以 不带参数进行实例化,例如MyClass(),看起来不像 对我来说最好的主意。 -
让
dataclasses自动生成__init__并实现__post_init__()是行不通的。这是行不通的,因为我需要能够构建没有参数的类,例如MyClass(),因为字段值将从另一个输入源(考虑本地文件或其他地方)设置;这意味着所有字段都是必需的,因此在这种情况下将它们注释为Optional将是错误的。我仍然需要能够支持用户输入可选的关键字参数,但是这些**kwargs将始终与数据类字段名称匹配,因此我希望通过某种方式让自动完成功能与我的 IDE (PyCharm) 一起使用
希望这篇文章能澄清期望和期望的结果。如果有任何问题或任何含糊不清的地方,请告诉我。
-
如果您这样做只是为了有关键字参数,请使用
@dataclass(kw_only=True)。 -
@user2357112 我不能使用
kw_only有几个不同的原因。例如,kw_only仍然根据需要制作所有参数,但我需要它们作为可选参数。此外,如果我们动态生成__init__方法,kw_only将不起作用。 -
看起来您生成自己的
__init__的唯一原因是复制dataclass可以为您提供的功能,如果您没有生成自己的__init__。 -
好的,但听起来大部分都由
__post_init__处理得更好。您可以让dataclass为您生成__init__并获得您正在寻找的所有IDE 自动完成优势。 -
看,我和下一个人一样喜欢重新发明轮子,但除此之外,在我看来,您的要求只是不一致。甚至如果有一种方法可以根据需要神奇地动态注释您的
__init__方法,您的类属性上的类型注释仍然是错误的。foo: str表示foo应该是一个字符串,并且绝不None。所以这些类型提示一开始就已经错了。不管你喜不喜欢,typing.Optional[str]或str | None是唯一正确的方法,如果一个实例的foo属性可以同时是str和None。
标签: python python-typing python-dataclasses