Mypy抱怨使用省略号的默认参数值不兼容



我正试图找到一种方法来区分参数是否已传递给该方法。例如,我有以下功能:

@dataclass
class Record:
id: int
name: str
completed_at: Optional[date] = None

records = [
Record(id=1, name="Foo", completed_at=date(2021, 1, 10)),
Record(id=2, name="Bar", completed_at=date(2021, 1, 11)),
]
def update_record(
id: int,
name: Optional[str] = None,
completed_at: Optional[date] = ...,  # type: ignore
):
record = next(record for record in records if record.id == id)
if name is not None:
record.name = name
if completed_at is not ...:
record.completed_at = completed_at

它就像一个符咒,但当我删除# type: ignore注释时,mypy会抱怨以下错误:

error: Incompatible default for argument
"completed_at" (default has type "ellipsis", argument has type
"Optional[date]")  [assignment]
... int, name: Optional[str] = None, completed_at: Optional[date] = ...

我尝试了一种解决方案;哨兵";类似对象:

DO_NOTHING = object()
def update_record(id, completed_at: Union[DO_NOTHING, None, date] = DO_NOTHING):
pass

但在我看来,这有点过于冗长。

有没有一种方法可以用一种不那么冗长的方式做得更好?

似乎来自输入模块的@overload正是我想要的。完整示例:

@dataclass
class Record:
id: int
name: str
completed_at: Optional[date] = None

records = [
Record(id=1, name="Foo", completed_at=date(2021, 1, 10)),
Record(id=2, name="Bar", completed_at=date(2021, 1, 11)),
]

@overload
def update_record(id: int):
...

@overload
def update_record(
id: int, name: Optional[str] = None, completed_at: Optional[date] = None
):
...

def update_record(id: int, *args, **kwargs):
record = next(record for record in records if record.id == id)
for field, value in kwargs.items():
setattr(record, field, value)

def test_update():
repository = Repository(records)
repository.update(1, name="Foobar")  # Do nothing with `completed_at` field
repository.update(2, completed_at=None)  # Set `completed_at` to None
# repository.update(2, completed_at="2021-01-12")  # Typing error
assert records == [
Record(id=1, name="Foobar", completed_at=date(2021, 1, 10)),
Record(id=2, name="Bar"),
]

我希望有人会发现它有用。

相关内容

  • 没有找到相关文章

最新更新