数据类描述符字段的类型注释应该是什么?

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

我正在开发一个类,用户应该能够以最方便的方式设置其字段,其中包括将字符串分配给任何字段。用户分配的值应自动转换为实际数据类型(例如,分配给字段

"2022-01-02"
date
应转换为
datetime.date
对象)。

为此,我选择了 Python 数据类模块的“描述符类型字段”方法。为了避免不必要和/或不受支持的转换,我检查 __annotations__ 以确定是否可以在不进行转换的情况下分配用户提供的值。

from typing import Optional

import datetime
from dataclasses import dataclass
from datetime import date
from decimal import Decimal


class Conversion:
    def __init__(self, *, conv, default=None):
        self._conv = conv
        self._default = default
        self._name = None
        self._prop = None

    def __set_name__(self, owner, name):
        self._prop = name
        self._name = "_" + name

    def __get__(self, obj, tp):
        # dataclasses determines default value by calling
        # descriptor.__get__(obj=None, tp=cls)
        if obj is None:
            return self._default

        return getattr(obj, self._name, self._default)

    def __set__(self, obj, value):
        tp = obj.__annotations__.get(self._prop)

        # Don't convert values which already match desired type
        if tp and isinstance(value, tp):
            setattr(obj, self._name, value)
        else:
            try:
                val = self._conv(value)
            except:
                raise ValueError(
                    f"Conversion error for '{self._name.lstrip('_')}': {value}"
                )

            setattr(obj, self._name, val)


@dataclass
class Entry:
    date: datetime.date = Conversion(conv=date.fromisoformat, default=date.today())
    amount: Optional[Decimal] = Conversion(conv=Decimal, default=None)


e = Entry()
print(e)
e.date = "2022-02-05"
e.amount = "11.02"
print(e)

输出如预期:

Entry(date=datetime.date(2024, 3, 7), amount=None) Entry(date=datetime.date(2022, 2, 5), amount=Decimal('11.02'))

这工作得很漂亮,我觉得这是非常干净和优雅的解决方案,但我注意到
文档

总是用描述符的类型而不是底层数据类型来注释描述符类型的字段。对我来说,这将是例如date: Conversion = Conversion(...)。数据类作者选择这样做有什么原因吗?我用数据类型注释字段是错误的吗?

    

python annotations python-dataclasses python-descriptors
1个回答
0
投票
date

的默认类型是

Conversion
,即注释
Conversion
是正确的。描述符是一个概念(或者更确切地说是协议的实现),而不是一种独特的对象类型。像 mypy 这样的 linter 会抱怨你的类型注释。
话又说回来,类型注释纯粹是可选的,而不是强制的,所以你可以在这里做你想做的事。就我个人而言,我会避免使用一个非常不具体的名称“Conversion”的描述符,该描述符对不同的数据字段执行不同的操作。特别是当底层数据类型不同时。

© www.soinside.com 2019 - 2024. All rights reserved.