类型提示:给原始数据类型添加别名是一种不好的做法吗? [英] Type hints: Is it a bad practice to alias primitive data types?
问题描述
在 Python 文档中输入 &类型提示我们有以下示例:
In Python documentation for typing & type hints we have the below example:
Vector = List[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
Vector
类型别名清楚地表明,类型别名对于简化复杂的类型签名很有用.
Vector
type alias clearly shows that type aliases are useful for simplifying complex type signatures.
但是,原始数据类型别名如何?
让我们对比两个函数签名的基本示例:
Let's contrast two basic examples of function signatures:
URL = str
def process_url(url: URL) -> URL:
pass
对比
def process_url(url: str) -> str:
pass
具有类型别名 URL
的原始类型 str
的版本是:
Version with type alias URL
for primitive type str
is:
- 自我记录(除其他外,现在我可以跳过记录返回值,因为它应该清楚地是一个网址),
- 抵抗类型实现更改(我可以稍后将 URL 切换为
Dict
或namedtuple
,而无需更改函数签名).
- self-documenting (among others, now I can skip documenting returned value, as it should be clearly an url),
- resistant to type implementation change (I can switch URL to be
Dict
ornamedtuple
later on without changing functions signatures).
问题是我找不到其他人遵循这种做法.我只是担心我无意中滥用类型提示来实现我自己的想法,而不是遵循它们的预期目的.
The problem is I cannot find anyone else following such practice. I am simply afraid that I am unintentionally abusing type hints to implement my own ideas instead of following their intended purpose.
2020-10 年的注意事项
Python 3.9 引入了灵活的函数和变量注释",允许进行如下注释:
Python 3.9 introduces "flexible function and variable annotations", which allows to make annotations like:
def speed_1(distance: "feet", time: "seconds") -> "miles per hour":
pass
def speed_2(
distance: Annotated[float, "feet"], time: Annotated[float, "seconds"]
) -> Annotated[float, "miles per hour"]:
pass
为了文档目的而呈现别名数据类型相当多余!
Which renders aliasing data types for documenting purposes rather redundant!
见:
- https://www.python.org/dev/peps/pep-0593/
- https://realpython.com/python39-new-features/#annotated-type-hints
推荐答案
使用别名来标记值的含义可能会产生误导和危险.如果只有一部分值有效,则应使用 NewType.
Using an alias to mark the meaning of a value can be misleading and dangerous. If only a subset of values are valid, a NewType should be used instead.
回想一下,类型别名的使用将两种类型声明为彼此等效.执行 Alias = Original
将使静态类型检查器将 Alias
视为 完全等效到 Original
所有情况.当您想简化复杂的类型签名时,这很有用.
Recall that the use of a type alias declares two types to be equivalent to one another. Doing
Alias = Original
will make the static type checker treatAlias
as being exactly equivalent toOriginal
in all cases. This is useful when you want to simplify complex type signatures.
简单的别名有两种作用:别名 URL = str
意味着任何 URL
是一个 str
也意味着 任何 str
都是一个 URL
——这通常是不正确的:URL 是一种特殊的 str
,没有任何一个可以代替它.别名 URL = str
是一种过于强烈的相等性声明,因为它无法表达这种区别.其实任何不看源码的检查都看不出区别:
Simple aliasing works both ways: the alias URL = str
means any URL
is a str
and also means any str
is a URL
– which is usually not correct: A URL is a special kind of str
and not any can take its place. An alias URL = str
is a too strong statement of equality, as it cannot express this distinction. In fact, any inspection that does not look at the source code does not see the distinction:
In [1]: URL = str
In [2]: def foo(bar: URL):
...: pass
...:
In [3]: foo?
Signature: foo(bar: str)
考虑在一个模块中为 Celsius = float
设置别名,而在另一个模块中为 Fahrenheit = float
设置别名.这表明将 Celsius
用作 Fahrenheit
是有效的,这是错误的.
Consider that you alias Celsius = float
in one module, and Fahrenheit = float
in another. This signals that it is valid to use Celsius
as Fahrenheit
, which is wrong.
除非您的类型do带有分离意义,否则您应该只取一个url: str
.名称表示含义,类型表示有效值.这意味着您的类型应该适合分隔有效值和无效值!
Unless your types do cary separative meaning, you should just take a url: str
. The name signifies the meaning, the type the valid values. That means that your type should be suitable to separate valid and invalid values!
使用别名缩短你的提示,但使用NewType细化它们.
Use aliases to shorten your hints, but use NewType to refine them.
Vector = List[float] # alias shortens
URL = NewType("URL", str) # new type separates
这篇关于类型提示:给原始数据类型添加别名是一种不好的做法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!