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

时间:2018-09-25 18:08:23

标签: python types typing

在Python文档中,有关键入和类型提示的内容如下example

Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

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

但是,别名原始数据类型又如何呢?

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

URL = str    

def process_url(url: URL) -> URL:
    pass

vs。

def process_url(url: str) -> str:
    pass

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

  • 自我记录(除其他外,现在我可以跳过记录返回值,因为它显然应该是网址)
  • 抵抗类型实现更改(我以后可以在不更改功能签名的情况下将URL切换为Dictnamedtuple)。

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

4 个答案:

答案 0 :(得分:2)

使用别名标记值的含义可能会引起误解并且很危险。应该使用NewType

  

回想一下,使用类型别名将两种类型声明为彼此等效。进行Alias = Original会使静态类型检查器将AliasOriginal 完全等同 。当您要简化复杂的类型签名时,这很有用。

简单的别名可以同时使用。 List[float]vector,而strURL –除外。 URL是一种特殊的str,URL不能代替它。别名是太强的平等声明,因为它不能表达这种区别。实际上,任何不查看源代码的检查都不会发现区别:

In [1]: def foo(bar: URL):
   ...:     pass
   ...:
In [2]: foo?
Signature: foo(bar: str)

请考虑在一个模块中为Celsius = float命名,在另一个模块中为Fahrenheit = float命名。这表示将Celsius用作Fahrenheit是有效的,这是错误的。

除非您的类型要做具有反义的含义,否则您应该只使用url: str。名称表示含义,类型表示有效值。这意味着您的类型应该适合分隔有效值和无效值!

使用别名缩短您的提示,但是使用NewType来优化

Vector = List[float]        # alias shortens
URL = NewType("URL", str)   # new type separates

答案 1 :(得分:1)

我不确定这个问题是否基于观点,但我认为总体共识将是这是一个好主意。您自己陈述收益,更不用说泛化代码等的能力了。

我敢冒险这在Python中并不常见,因为该语言本身并不是很严格。另外,该变量已被称为url-这很容易解释。您可能会说您可能有一个叫做json_response之类的东西,并且您希望它是一个url,并且您的方法肯定会很清楚,但是由于Python鼓励使用鸭子输入,因此经常使用代码无论如何都会给出此提示,对于不体贴的用户,使用类型别名将是额外的安全性。实际上,这只是普通做法,没有好的“做到这一点!”解释。

终点-从某种意义上说,类型别名是面向对象编程的最原始版本。您正在明确说明此对象的期望属性,在这种情况下,字符串应为有效的URL。

答案 2 :(得分:1)

我想人们可能会问自己的问题是“目的”。

我坚信Python的可读性至关重要。 考虑到这一点,即使对于基本类型,类型提示也可以。如果类型被虚拟的类似于“枚举”的类型掩盖,并且进行一些自我记录,那就更好了。

话虽如此-我个人会选择第一个: 网址= str
def process_url(URL:URL)-> URL:     通过

答案 3 :(得分:0)

我不知道什么是普遍的看法,但我认为这是对经常重复的事情的一种好习惯,因为它为您提供了一个定义含义的地方。

广告重复,考虑到您有很多功能,例如

def foo(url : str):
    """
    :param url: explaining url
    """

您最终将在每个函数中定义url,因此您可以这样做

def foo(x : Url):
   pass

类型别名的麻烦在于您无法记录它,因此我来关注

class _Url(str):
    """
    Here you can document the type
    """

Url = typing.Union[_Url, str]

这会让你

  1. 从呼叫站点的角度来看类型别名的行为(无需强制转换)

  2. ,同时允许您在类型和

  3. 中表达值的含义
  4. 能够记录类型本身

唯一的缺点是,工会的含义不是立即显而易见,而是技术上正确,我认为目前可以做到最好。