我想创建一个函数签名,其中
undefined
值和 None
彼此不同。
所以例如:
undefined = object()
def update(id: str, title: str = undefined, description: str):
data = {
"title": title,
"description": description,
}
// remove undefined key-value pairs from data
for key in data.keys():
if data[key] is undefined:
del data[key]
# send data to the server for further processing
显然这样我得到类型冲突和以下错误:
Incompatible default for argument "description" (default has type "object", argument has type "str")
从上面的例子可以看出,传递
None
是完全有效的,因为它将被替换为JSON的null
值,而如果传递undefined
,则该值将不会被考虑在数据中。
我尝试定义未定义的类型,例如:
UndefinedStr = typing.Union[str, undefined]
但是我得到以下错误:
Variable "undefined" is not valid as a type
我想那是因为
undefined
是一个实例。因此,作为一种解决方法,我可以这样做:
class Undefined:
pass
undefined = Undefined()
UndefinedStr = Union[str, Undefined]
这个解决方案有效,但对我来说有点太宽泛了。
所以我的问题是什么是键入提示这个未定义用例的最佳方式?
你最后的尝试似乎是合理的,但如果你觉得创建一个新类型只是为了在你只需要它的一个实例时用类型注释一个参数太宽泛,你可以使用
typing.Literal
来限制参数一个特定的对象。由于 typing.Literal
只接受文字或 Enum
对象,一个可行的解决方案是创建一个单例 Enum
类,目的是:
from typing import Literal
from enum import Enum
Undefined = Enum('Undefined', ['undefined'])
undefined = Undefined.undefined
def update(id: str, title: str | Literal[Undefined.undefined] = undefined):
...
通过mypy的代码演示:https://mypy-play.net/?mypy=latest&python=3.11&gist=d44b405f144405354e380162df49ddbe
由于您将
undefined
用作单例并使用 is
进行测试,因此类型和值并不重要。那么为什么不把它变成一个字符串呢?我会选择一个明确无误的无效值,这样如果以后的错误让它溜走,你就可以抓住它。
undefined = 'undefined ' * 10
这可以使用 NewType (https://docs.python.org/3/library/typing.html#typing.NewType) 来实现。 结合您的一项尝试,我们可以通过以下方式实现目标:
Undefined = typing.NewType('undefined', str)
UndefinedStr = typing.Union[str, Undefined]
然后使用新类型的实例定义函数;
undef = undefined('undefined')
def update(id: str, description: UndefinedStr = undef, title: str = 'title'):
这将作为静态类型检查器的类型提示。在运行时
if data[key] is undef # comparison with instance of str type, but will return True when data[key] = undef (@Brian's comment and doc of NewType).
链接到演示代码; https://mypy-play.net/?mypy=latest&python=3.11&gist=2b6e96a21a1eff30874cdf81938d8bdc
到目前为止,您采用的方法是使用对象实例来表示未定义的值并使用自定义类型提示来处理它,这是解决问题的有效方法。但是,正如您所指出的,它可能感觉有点太宽泛了。
另一种方法是使用 None 表示值的缺失,使用 Ellipsis 表示明确未定义的值。省略号是 Python 中的内置常量,通常用作占位符值。它可以在类型提示中使用,以指示一个明确未定义的值,而不是 None,它表示没有值。
这是使用这种方法的示例函数签名:
def update(id: str, title: str = ..., description: str) -> None:
data = {
"title": title,
"description": description,
}
# remove undefined key-value pairs from data
data = {k: v for k, v in data.items() if v is not Ellipsis}
# send data to the server for further processing
...
在此示例中,title 指定了一个省略号 (...) 默认值,这表明该值未明确定义。 description 参数是必需的,因此它没有默认值。函数的返回类型指定为 None 表示函数不返回值。
您还可以定义一个包含省略号作为可能值的自定义类型提示,如下所示:
from typing import Union
Undefined = Ellipsis
UndefinedStr = Union[str, Undefined]
这将允许您使用 UndefinedStr 作为可以显式未定义的字符串值的类型提示。
总的来说,选择使用哪种方法取决于您的个人喜好和特定用例的要求。这两种方法都是有效的,可以有效地用于区分未定义和无值。