类型提示:给原始数据类型添加别名是一种不好的做法吗?

Cap*_*ero 13 python types type-hinting

在 Python 的输入和类型提示文档中,我们有以下示例

Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]
Run Code Online (Sandbox Code Playgroud)

Vector 类型别名清楚地表明类型别名对于简化复杂的类型签名很有用。

然而,给原始数据类型别名又如何呢?

让我们对比两个函数签名的基本示例:

URL = str    

def process_url(url: URL) -> URL:
    pass
Run Code Online (Sandbox Code Playgroud)

对比

def process_url(url: str) -> str:
    pass
Run Code Online (Sandbox Code Playgroud)

URL原始类型具有类型别名的版本str是:

  • 自我记录(除其他外,现在我可以跳过记录返回值,因为它显然应该是一个 url),
  • 抵抗类型实现更改(我可以在不更改函数签名的情况下切换 URLDictnamedtuple稍后)。

问题是我找不到其他人遵循这种做法。我只是担心我无意中滥用类型提示来实现我自己的想法,而不是遵循它们的预期目的。


2020-10 年注意事项

Python 3.9 引入了“灵活的函数和变量注释”,它允许进行如下注释:

Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]
Run Code Online (Sandbox Code Playgroud)

这使得用于文档目的的别名数据类型相当多余!

看:

Mis*_*agi 17

使用别名来标记值的含义可能会产生误导和危险。如果只有一部分值有效,则应改用NewType

回想一下,类型别名的使用将两种类型声明为彼此等效。这样做Alias = Original将会使静态类型检查治疗Alias完全等同Original 在所有情况下。当您想要简化复杂的类型签名时,这很有用。

简单的别名有两种作用:别名URL = str意味着 anyURL是 a str ,也意味着anystrURLa——这通常是不正确的:URL 是一种特殊的类型,str没有任何可以取代它的位置。别名URL = str是一种过于强烈的平等声明,因为它无法表达这种区别。其实任何不看源码的检查都看不出区别:

In [1]: URL = str
In [2]: def foo(bar: URL):
   ...:     pass
   ...:
In [3]: foo?
Signature: foo(bar: str)
Run Code Online (Sandbox Code Playgroud)

考虑Celsius = float在一个模块和Fahrenheit = float另一个模块中使用别名。这表明使用Celsiusas是有效的Fahrenheit,这是错误的。

除非你的类型卡里分离式的意义,你应该采取url: str。名称表示含义,类型表示有效值。这意味着您的类型应该适合分隔有效值和无效值!

使用别名来缩短你的提示,但使用 NewType 来细化它们。

Vector = List[float]        # alias shortens
URL = NewType("URL", str)   # new type separates
Run Code Online (Sandbox Code Playgroud)

  • 正是因为 Python 支持鸭子类型,所以类型提示应该尽可能有意义。如果任何“str”可能有效,那么这么说并没有什么错!通过不使用基本类型,隐含了一些特殊规则,这些规则在未实际定义时可能会产生误导 - 即使该规则只是“提供“URL”的函数的值”。 (4认同)
  • @CapedHero - 您可以使用 mypy 之类的工具强制执行静态类型 - 它是可配置的,因此您可以根据需要完全禁止所有动态类型。无论如何,在这种情况下,我同意在这里使用 NewType 是正确的——这样,类型检查器可以强制约束并非所有字符串都是 URL。 (2认同)

kab*_*nus 3

我不确定这个问题是否基于意见,但我有一种感觉,总的来说,这是一个好主意。您自己陈述了好处,更不用说概括代码的能力等。

我敢说这在 Python 中并不常见,因为该语言本身的限制并不多。此外,该变量已经被调用url——这是非常不言自明的。你可能会争辩说你可能有一个叫json_response或类似的东西,你希望它是一个url,你的方法肯定会清楚地表明这一点,但由于Python鼓励鸭子类型,代码使用通常会给出这个提示,无论如何,使用类型别名会对于不体贴的用户来说只是额外的安全。这实际上只是常见的做法,没有好的“这样做!” 解释。

从某种意义上说,最终的点类型别名是面向对象编程的最原始版本。您正在明确您期望该对象具有哪些属性,在本例中,字符串应该是有效的 URL。