在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
的版本是:
Dict
或namedtuple
)。问题是按照这种做法我找不到其他人。我只是担心我会无意间滥用类型提示来实现我自己的想法,而不是遵循他们的预期目的。
答案 0 :(得分:2)
使用别名标记值的含义可能会引起误解并且很危险。应该使用NewType。
回想一下,使用类型别名将两种类型声明为彼此等效。进行
Alias = Original
会使静态类型检查器将Alias
与Original
完全等同 。当您要简化复杂的类型签名时,这很有用。
简单的别名可以同时使用。 List[float]
是vector
,而str
是URL
–除外。 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]
这会让你
从呼叫站点的角度来看类型别名的行为(无需强制转换)
,同时允许您在类型和
能够记录类型本身
唯一的缺点是,工会的含义不是立即显而易见,而是技术上正确,我认为目前可以做到最好。