我正在修改一个应用程序,try 为我的应用程序模型使用PYDANIC,为我的数据库模型使用SQLAlChemy.

我有现有的类,我按照别人教我的那样在__init__方法中定义了属性:

class Measure:
    def __init__(
        self,
        t_received: int,
        mac_address: str,
        data: pd.DataFrame,
        battery_V: float = 0
    ):
        self.t_received = t_received
        self.mac_address = mac_address
        self.data = data
        self.battery_V = battery_V

在PYDANIC和SQLAlChemy中,在文档之后,我必须定义__init__方法之外的那些属性,例如在PYDANIC中:

import pydantic

class Measure(pydantic.BaseModel):
    t_received: int
    mac_address: str
    data: pd.DataFrame
    battery_V: float

为什么会这样呢?这不是很糟糕的做法吗?对该类的其他方法(类方法、静态方法、属性...)有影响吗?

注意,这也是very unhandy,因为当我实例化该类的对象时,我没有得到关于构造函数需要哪些参数的建议!

推荐答案

直接在类命名空间中定义类的属性是完全可以接受的,并且对于您提到的包本身并不特殊.由于类命名空间(以及其他内容)本质上是该类实例的蓝图,因此在那里定义属性实际上可能是有用的,例如,当您想要以一致的方式在单个位置为所有公共属性提供类型注释时.

还要考虑公共属性不一定需要由类的构造函数中的参数反映.例如,这是完全合理的:

class Foo:
    a: list[int]
    b: str

    def __init__(self, b: str) -> None:
        self.a = []
        self.b = b

换句话说,仅仅因为某些东西是公共属性,并不意味着它必须在初始化时由用户提供.更不用说受保护/私有属性了.

Pydtic的特殊之处(以您的例子为例)是BaseModel的元类以及类本身对类命名空间中定义的属性进行了大量的魔术处理.PYDANIC将模型的典型属性称为"字段",并且在初始化过程中可以根据在类名称空间中定义的那些字段执行特殊判断.例如,构造函数必须接收与您定义的非可选字段相对应的关键字参数.

from pydantic import BaseModel


class MyModel(BaseModel):
    field_a: str
    field_b: int = 1


obj = MyModel(
    field_a="spam",  # required
    field_b=2,       # optional
    field_c=3.14,    # unexpected/ignored
)

如果我在构造MyModel实例的过程中省略了field_a,则会引发错误.同样,如果我试图通过field_b="eggs",就会出现错误.

因此,您不编写自己的__init__方法的事实是feature个平凡的东西提供给您.您只需定义字段,相应的构造函数就已经"神奇"地为您准备好了.

至于你提到的缺点,你不会得到任何自动建议,默认情况下,所有IDE都是如此.静态类型判断器不能理解动态构造函数并简单地推断需要什么参数.目前,这是通过扩展解决的,如mypy pluginPyCharm plugin.也许很快就会成为PEP 681年的@dataclass_transform名室内设计师 将对类似的包进行标准化,从而提高静态类型判断器的支持.

同样值得注意的是,即使标准库的dataclasses也只能在类型判断器中通过special extensions工作.

对于您的另一个问题,显然对此类类的方法有一些影响(通过设计),尽管细节并不总是显而易见的.当然,您不应该简单地编写您自己的__init__方法,而不小心在其中正确地调用超类‘__init__.此外,@property-setters目前有don't work as you would expect it个(尽管在Pydtic模型上使用属性是否有意义还有待商榷).

总而言之,这种方法不仅是not个糟糕的做法,它是一个减少样板代码的好主意,而且现在它非常普遍,这一事实证明了非常流行的和成熟的包(如前面提到的Pydtic,以及例如SQLAlChemy、Django和其他)在一定程度上使用了这种模式.

Python相关问答推荐

不理解Value错误:在Python中使用迭代对象设置时必须具有相等的len键和值

在vscode上使用Python虚拟环境时((env))

多处理队列在与Forking http.server一起使用时随机跳过项目

如何在Python中获取`Genericums`超级类型?

解决调用嵌入式函数的XSLT中表达式的语法移位/归约冲突

为什么\b在这个正则表达式中不解释为反斜杠

使用Python查找、替换和调整PDF中的图像'

如何将数据帧中的timedelta转换为datetime

用SymPy在Python中求解指数函数

Gekko中基于时间的间隔约束

如何使用正则表达式修改toml文件中指定字段中的参数值

我对这个简单的异步者的例子有什么错误的理解吗?

在用于Python的Bokeh包中设置按钮的样式

多个矩阵的张量积

TypeError:';Locator';对象无法在PlayWriter中使用.first()调用

替换包含Python DataFrame中的值的<;

遍历列表列表,然后创建数据帧

具有不同坐标的tkinter canvs.cocords()和canvs.moveto()

解析CSV文件以将详细信息添加到XML文件

迭代工具组合不会输出大于3的序列