Python输入模块–有效使用类型检查器
自从Python 3.5引入以来,Python的键入模块试图提供一种提示类型的方法,以帮助静态类型检查器和linter准确地预测错误。
由于Python必须在运行时确定对象的类型,因此开发人员有时很难找出代码中到底发生了什么。
即使像PyCharm IDE这样的外部类型检查器也无法产生最佳结果。
根据StackOverflow上的答案,平均而言,平均只有大约50%的时间可以正确预测错误。
Python尝试通过引入所谓的类型提示(类型注释)来缓解这种问题,以帮助外部类型检查器识别任何错误。
对于程序员来说,这是在编译时提示使用的对象类型的好方法,并确保类型检查器正常工作。
这也使Python代码对其他读者来说更具可读性和鲁棒性!
注意:这不会在编译时进行实际的类型检查。
如果返回的实际对象与提示的类型不同,则不会出现编译错误。
这就是为什么我们使用外部类型检查器(例如mypy)来识别任何类型错误的原因。
建议的前提条件
为了有效地使用"类型"模块,建议您使用外部类型检查器/衬垫来检查静态类型匹配。
用于Python的最广泛使用的类型检查器之一是mypy,因此,建议您在阅读本文的其余部分之前先安装它。
我们已经介绍了Python类型检查的基础。
您可以先阅读本文。
在本文中,我们将使用" mypy"作为静态类型检查器,可以通过以下方式安装它:
pip3 install mypy
您可以对任何Python文件运行" mypy"以检查类型是否匹配。
就像您正在"编译" Python代码一样。
mypy program.py
调试错误后,可以使用以下命令正常运行该程序:
python program.py
现在我们已经满足了前提条件,让我们尝试使用该模块的某些功能。
类型提示/类型注释
关于功能
我们可以注释一个函数以指定其返回类型和其参数的类型。
def print_list(a: list) -> None: print(a)
这通知类型检查器(在我的情况下为" mypy"),我们有一个函数" print_list()",该函数将" list"作为参数并返回" None"。
def print_list(a: list) -> None: print(a) print_list([1, 2, 3]) print_list(1)
首先让我们在类型检查器" mypy"上运行此代码:
vijay@theitroad:~ $mypy printlist.py printlist.py:5: error: Argument 1 to "print_list" has incompatible type "int"; expected "List[Any]" Found 1 error in 1 file (checked 1 source file)
不出所料,我们得到一个错误;因为第5行的参数为" int",而不是" list"。
关于变量
从Python 3.6开始,我们还可以注释变量的类型,并提及类型。
但这不是强制性的,如果您希望在函数返回之前更改变量的类型。
# Annotates 'radius' to be a float radius: float = 1.5 # We can annotate a variable without assigning a value! sample: int # Annotates 'area' to return a float def area(r: float) -> float: return 3.1415 * r * r print(area(radius)) # Print all annotations of the function using # the '__annotations__' dictionary print('Dictionary of Annotations for area():', area.__annotations__)
mypy的输出:
vijay@theitroad: ~ $mypy find_area.py && python find_area.py Success: no issues found in 1 source file 7.068375 Dictionary of Annotations for area(): {'r': <class 'float'>, 'return': <class 'float'>}
这是推荐的使用" mypy"的方法,首先使用类型注释,然后再使用类型检查器。
类型别名
"类型"模块为我们提供了类型别名,该类型别名是通过为别名分配类型来定义的。
from typing import List # Vector is a list of float values Vector = List[float] def scale(scalar: float, vector: Vector) -> Vector: return [scalar * num for num in vector] a = scale(scalar=2.0, vector=[1.0, 2.0, 3.0]) print(a)
输出
vijay@theitroad: ~ $mypy vector_scale.py && python vector_scale.py Success: no issues found in 1 source file [2.0, 4.0, 6.0]
在上面的代码段中," Vector"是一个别名,代表浮点值的列表。
我们可以在别名上键入提示,这就是上面程序的作用。
此处提供可接受别名的完整列表。
让我们再看一个示例,该示例检查字典中每个key:value对,并检查它们是否与name:email格式匹配。
from typing import Dict import re # Create an alias called 'ContactDict' ContactDict = Dict[str, str] def check_if_valid(contacts: ContactDict) -> bool: for name, email in contacts.items(): # Check if name and email are strings if (not isinstance(name, str)) or (not isinstance(email, str)): return False # Check for email [email protected] if not re.match(r"[a-zA-Z0-9\._\+-]+@[a-zA-Z0-9\._-]+\.[a-zA-Z]+$", email): return False return True print(check_if_valid({'vijay': '[email protected]'})) print(check_if_valid({'vijay': '[email protected]', 123: '[email protected]'}))
mypy的输出
vijay@theitroad:~ $mypy validcontacts.py validcontacts.py:19: error: Dict entry 1 has incompatible type "int": "str"; expected "str": "str" Found 1 error in 1 file (checked 1 source file)
其中我们在mypy
中得到一个静态编译时错误,因为第二个字典上的name
参数是一个整数(123)。
因此,别名是从" mypy"强制执行准确类型检查的另一种方法。
使用NewType()创建用户定义的数据类型
我们可以使用NewType()函数来创建新的用户定义类型。
from typing import NewType # Create a new user type called 'StudentID' that consists of # an integer StudentID = NewType('StudentID', int) sample_id = StudentID(100)
静态类型检查器会将新类型视为原始类型的子类。
这有助于帮助捕获逻辑错误。
from typing import NewType # Create a new user type called 'StudentID' StudentID = NewType('StudentID', int) def get_student_name(stud_id: StudentID) -> str: return str(input(f'Enter username for ID #{stud_id}:\n')) stud_a = get_student_name(StudentID(100)) print(stud_a) # This is incorrect!! stud_b = get_student_name(-1) print(stud_b)
mypy的输出
vijay@theitroad:~ $mypy studentnames.py studentnames.py:13: error: Argument 1 to "get_student_name" has incompatible type "int"; expected "StudentID" Found 1 error in 1 file (checked 1 source file)
任何类型
这是一种特殊类型,通知静态类型检查器(在我的情况下为" mypy"),每种类型都与此关键字兼容。
考虑我们以前的print_list()
函数,现在可以接受任何类型的参数。
from typing import Any def print_list(a: Any) -> None: print(a) print_list([1, 2, 3]) print_list(1)
现在,当我们运行mypy
时将没有任何错误。
vijay@theitroad:~ $mypy printlist.py && python printlist.py Success: no issues found in 1 source file [1, 2, 3] 1
没有返回类型或者参数类型的所有函数都将隐式默认使用Any
。
def foo(bar): return bar # A static type checker will treat the above # as having the same signature as: def foo(bar: Any) -> Any: return bar
因此,您可以使用Any混合静态和动态类型的代码。