Python 是当今使用最流行的编程语言之一,这是有充分理由的:它是开源的,它具有广泛的用途(例如 Web 编程、商业应用程序、游戏、科学编程等等),并且它拥有一个充满活力和敬业的社区来支持它。正是由于这个社区,我们才能在 Python 包索引 (PyPI) 中获得如此庞大、多样化的软件包,以扩展和改进 Python 并解决不可避免出现的小故障。
在本系列中,我们研究了七个 PyPI 库,它们可以帮助您解决常见的 Python 问题。今天,在最后一篇文章中,我们将看看 mypy,“Python 超级 Linter”。
mypy
Python 是一种“动态类型”语言。 但是,有时让其他主体(包括机器人和人类)知道期望的类型会很好。 传统上,人类的优先级更高:函数的输入和输出类型在文档字符串中进行了描述。 MyPy 允许您将机器人置于同等地位,让他们知道预期的类型。
让我们看看下面的代码
def add_one(input):
return input + 1
def print_seven():
five = "5"
seven = add_one(add_one(five))
print(seven)
调用 print_seven 会引发 TypeError,告知我们无法将字符串和数字相加:我们无法将“5”和 1 相加。
但是,我们只有在运行代码后才能知道这一点。 如果代码正确,运行代码会在屏幕上生成打印输出:一种副作用。 从副作用来看,这是一种相对无害的副作用,但仍然是一种副作用。 有可能在不冒任何副作用的风险的情况下做到这一点吗?
我们只需要让机器人知道会发生什么。
def add_one(input: int) -> int:
return input + 1
def print_seven() -> None:
five = "5"
seven = add_one(add_one(five))
print(seven)
我们使用类型注解来表示 add_one 期望一个整数并返回一个整数。 这不会改变代码的作用。 但是,现在我们可以要求一个安全的机器人来为我们找到问题。
$ mypy typed.py
typed.py:6: error: Argument 1 to "add_one" has incompatible type "str"; expected "int"
我们对我们做错的事情有一个清晰易懂的解释。 让我们修复 print_seven。
def print_seven() -> None:
five = 5
seven = add_one(add_one(five))
print(seven)
如果我们在上面运行 mypy,就不会有任何抱怨;我们修复了错误。 令人高兴的是,这也导致了可以正常工作的代码。
当然,Python 类型系统可能会非常深入。 遇到类似这样的签名并不罕见
from typing import Dict, List, Mapping, Sequence
def unify_results(
results1: Mapping[str, Sequence[int]],
results2: Mapping[str, Sequence[int]]
) -> Dict[str, List[int]]:
pass
在这些情况下,请记住一切都是对象:是的,甚至是类型。
ResultsType = Mapping[str, Sequence[int]]
ConcreteResultsType = Dict[str, List[int]]
def unify_results(results1: ResultsType, results2: ResultsType) -> ConcreteResultsType:
pass
我们将输入类型定义为抽象类型(使用 Mapping 和 Sequence)。 这允许传入,例如,将字符串映射到元组的 defaultdict。 这通常是正确的选择。 我们还选择在签名中保证具体的返回类型。 这更具争议性:有时,为了允许将来更改返回类型,保证更少的东西是有用的。
MyPy 允许渐进式注解:不必一次注解所有内容。 没有任何注解的函数将不会进行类型检查。
前进并注解吧!
评论已关闭。