数据类描述符字段的类型注释应是什么?
我正在开发一个类,用户应该能够以最方便的方式设置它的字段,这包括将字符串赋值给任何字段。用户赋值后,这些值应该自动转换成实际的数据类型(比如说,用户把 "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(...)
。请问,数据类的作者为什么选择这样做?我这样用数据类型来注解字段是否有问题?
1 个回答
1
从编程的角度来看,在你的例子中,date
的默认类型是Conversion
,也就是说,使用Conversion
这个注解是正确的。不过,像mypy这样的代码检查工具会对你的类型注解提出警告。
不过,类型注解其实是可选的,并不是强制要求的,所以你可以随意选择。在我个人看来,我会避免使用一个非常模糊的名字“Conversion”作为描述符,因为它对不同的数据字段做的事情不一样。尤其是当底层数据类型不同时,这样会让人更困惑。