mypy
mypy是一个可以帮助我们检查类型的第三方库,通过 pip insatll mypy
安装,然后执行 mypy <py文件>
即可自动帮我们检测数据类型是否使用正确.例如我们有这样一段代码。
test: int = "Hello World!"
我们期望创建一个int类型的变量, 但我们却给变量赋值了字符串。此时利用mypy检查就会提示我们:test.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] Found 1 error in 1 file (checked 1 source file)
。返回信息告诉了我们不符预期的数据类型,并告知了我们具体文件及行号。
此时我们对上述代码行稍作改动
test: int = 1
我们为变量传入正确的int类型数据。并再次用mypy检查,此次返回的信息为:Success: no issues found in 1 source file
。
由此可见如果我们需要严格控制数据类型,mypy会是一个不错的检测工具。
typing
在有些情况下我们希望我们的数据类型提示更灵活以应对各种情况,此时就可以使用typing来帮助我们。
- 注意1:typing是在Python3.5才被加入到标准库中,这意味着您需要保证Python版本至少为3.5
- 注意2:虽然typing模块提供了类型提示的支持,但它不会对代码的实际执行产生任何影响。它主要用于静态类型检查工具、IDE和其他工具来提供类型推断和类型错误检查支持。
1. List
List
可以表明我们需要一个列表,并且我们可以统一指定列表内元素的数据类型。
from typing import List
test1: List = ["Hello World!", 0, True] # OK
test2: List[int] = [0, 1, 2] # OK
test3: List[int] = ["Hello World!", 0, True] # Fails type check
2. Tuple
当需要传入一个元祖时我们可以使用 Tuple
,但 Tuple
与 List
的使用略有不同,Tuple
可以指定元组内每一个元素的数据类型
from typing import Tuple
test1: Tuple = ("Hello World!", 0, True) # OK
test2: Tuple[str, int, bool] = ("Hello World!", 0, True) # OK
test3: Tuple[str, int, bool] = ("Hello World!", 0, True, "spare") # Fails type check
test4: Tuple[str, int, bool] = ("Hello World!", 0) # Fails type check
3. Dict & TypedDict
Dict
可以表明我们需要一个字典,并且我们可以统一设置字典内键值对的数据类型。
from typing import Dict
test1: Dict = {"str": "Hello World!"} # OK
test2: Dict[str, bool] = {"like sports" : True, "like music" : True} # OK
test3: Dict[str, bool] = {"like sports" : True, 0 : True} # Fails type check
test4: Dict[str, bool] = {"like sports" : True, "like music" : "True"} # Fails type check
注意:上述代码段中 []
内的 str
指的是字典中键的数据类型,bool
指的是值的数据类型。
对于字典的数据类型提示我们其实有更灵活的方式,TypedDict
可以帮助我们自定义一个拥有不同数据类型键值对的字典。
from typing import TypedDict, List
# 方法一
MyType = TypedDict('MyType', {"title": str, "todo": List[int]})
# 方法二(推荐)
class MyType(TypedDict):
title: str
todo: List[int]
test1: MyType = {"title": "Hello World!", "todo": [1, 2]} # OK
test2: MyType = {"title": "Hello World!"} # Fails type check
test3: MyType = {"title": "Hello World!", "todo": [1, 2], "spare": "spare"} # Fails type check
test4: MyType = {"title": "Hello World!", "changed": [1, 2]} # Fails type check
用上述的方法一和方法二都可以为字典自定义一个提示数据类型的方法,但如果使用了这两种提示方法就意味着我们必须要在创建字典时包含所有的指定的key。若我们不想如此严格的要求某些key的存在,我们可以设置 total=False
(注意:我们仍然不可以写入未指定的key)。
from typing import TypedDict, List
class MyType(TypedDict, total=False):
title: str
todo: List[int]
test1: MyType = {"title": "Hello World!", "todo": [1, 2]} # OK
test2: MyType = {"title": "Hello World!"} # OK
test3: MyType = {"title": "Hello World!", "todo": [1, 2], "spare": "spare"} # Fails type check
test4: MyType = {"title": "Hello World!", "changed": [1, 2]} # Fails type check
4. Union
当我们输入的参数类型有多种可选项时可以使用 Union
from typing import Union
test1: Union[str, int] = "Hello World!" # OK
test2: Union[str, int] = 0 # OK
test3: Union[str, int] = 1.0 # Fails type check
从Python3.10开始 Union
被替换为 |
意味着 Union[X, Y]
等价于 X | Y
,这样我们意味着甚至都不需要导入typing。
test1: str | int = "Hello World!" # OK
test2: str | int = 0 # OK
test3: str | int = 1.0 # Fails type check
5. Callable
当你需要表明某个函数的参数是函数时可以使用 Callable
作为类型提示。
from typing import Callable
def fun() -> None:
return None
def main(fun: Callable) -> None:
return fun()
main(fun()) # OK
main(1) # Fails type check
6. Any
当数据可以为任何类型的时候可以使用 Any
作为数据提示
from typing import Any
test1: Any = "Hello World!" # OK
test2: Any = 0 # OK
7. Optional
当函数的参数有默认值,导致参数不是必须要传入的,那么你可以尝试使用 Optional
来做到类型提示
from typing import Optional
def test(value: Optional[int] = None):
return value
test() # OK
test(0) # OK
test("Hello World!") # Fails type check
从上面的代码我们发现, Optional
作用几乎和其他带参数的数据类型提示等价,例如:value: int = 0
。
但其实是有些许区别的,假设我们声明参数时为 value: int = None
用静态检查工具去检查代码时会返回错误给我们。而我们为 value
指定数据类型为 Optional[int]
但默认值为 None
经过静态检查工具也不会返回错误。这其实是因为 Optional[X]
本质是等价于 Union[X, None]
的。
8. Sequence
Sequence
所提示的是任何可以被索引的数据: 列表,元祖,字符串等。注意:[]
中只可以指定一个数据类型,意味着在我们传入的列表或者元祖等可索引数据时,内部元素的数据类型需要是统一的。
from typing import Sequence
test1: Sequence[str] = "Hello World!" # OK
test2: Sequence[str] = ["one", "two", "three"] # OK
test3: Sequence[str] = ("one", "two", "three") # OK
test4: Sequence[str] = ["one", "two", 3] # Fails type check
test5: Sequence[str] = ("one", "two", True) # Fails type check
9.特殊的玩法
- 灵活运用
|
其实可以发现单独的使用类型提示是会遇到限制的,例如当我们设定test: List[str]
之后我们创建列表时就只能在列表中加入字符串元素,但我们在生产中往往列表不会只包含一种类型的元素。当我们希望列表中既可以有字符串又可以有整型时我们用|
帮助我们做类型提示。from typing import List test: List[str|int] = ["Hello World!", 0] # OK
- 自定义数据类型
上文中我们在使用TypedDict
时做的其实就是一种自定义类型提示。不仅仅TypedDict
可以这样做,其他的类型提示方法都可以被自定义为自己的类型提示(注意:只有TypedDict
需要继承实现)。在特定的情况下使用封装好的类型提示或许可以使项目中参数和变量更直观。from typing import List MyType = List[str] test1: MyType = ["one", "two"] # OK test2: MyType = ["one", 2] # Fails type check