Python 3.6+ 版本
加入了对 类型提示
的支持
这些 类型提示
是一种新的语法, 用来声明一个变量的类型
没啥用的 *前言* 废话
- Python 是一种动态类型语言,这意味着我们在编写代码的时候更为自由,运行时不需要指定变量类型
- 但是与此同时 IDE 无法像静态类型语言那样分析代码,及时给我们相应的提示,比如字符串的 split 方法
1 2
| def split_str(s); strs = s.split(",")
|
由于不知道参数 s 是什么类型,所以当你敲 s. 的时候不会出现 split 的语法提示
解决上述问题,类型提示
Python 3.5、3.6 新增了两个特性 PEP 484 和 PEP 526
这些新特性不会影响语言本身,只是增加一点提示
简单类型
1 2 3
| num: int = 0 bool_var: bool = True dict_var: dict = {}
|
1 2 3
| def get_name_with_age(name: str, age: int): name_with_age = name + " is this old: " + str(age) return name_with_age
|
- 变量标注了类型, 传错类型运行
不会报错
, 只是在 IDE 有智能语法 ⚠️ 警告提示 - 类型提示更像一个规范约束, 而不是语法限制
嵌套类型
使用 Python 的 typing 标准库来声明这些类型以及子类型
列表
- 变量 items 是一个 list,并且这个列表里的每一个元素都是 str
1 2 3 4 5
| from typing import List
def process_items(items: List[str]): for item in items: print(item)
|
元组和集合
- 变量 items_t 是一个 tuple,其中的前两个元素都是 int 类型, 最后一个元素是 str 类型
- 变量 items_s 是一个 set,其中的每个元素都是 bytes 类型
1 2 3 4
| from typing import Set, Tuple
def process_items(items_t: Tuple[int, int, str], items_s: Set[bytes]): return items_t, items_s
|
元组打包、解包
打包
1 2
| t1: Tuple[int, ...] = (1,2,3) t2: Tuple[int, ...] = 1,2,3
|
解包
1 2 3 4 5 6 7 8
| from typing import List
header: str kind: int body: List[str] header, kind, body = ("str", 123, ["1", "2", "3"])
print(header, kind, body)
|
字典
变量 prices 是一个 dict:
- 这个 dict 的所有键为 str 类型
- 这个 dict 的所有值为 float 类型
1 2 3 4 5 6
| from typing import Dict
def process_items(prices: Dict[str, float]): for item_name, item_price in prices.items(): print(item_name) print(item_price)
|
类作为类型
假设有 Person
类, 拥有 name 属性, 则可以将一个变量声明为 Person
类型
1 2 3 4 5 6
| class Person: def __init__(self, name: str): self.name = name
def get_person_name(one_person: Person): return one_person.name
|
返回值类型
1 2
| def say_hi(name: str) -> str: return f'Hello {name}!'
|
1 2
| def add(first: int = 10, second: float = 5.5) -> float: return first + second
|
如果要避免循环导入或者注解早于对象定义的情况, 可以用字符串代替类型, 效果相同
1 2 3 4 5 6
| def hello(p: 'Person') -> str: return f'Hello, {p.name}'
class Person: def __init__(self, name: str): self.name = name
|
如果你实在不知道某个类型注解应该怎么写时,这里还有个最后的逃生通道
任何类型都与 Any 兼容。当然如果你把所有的类型都注解为 Any 将毫无意义,因此 Any 应当尽量少使用。
1 2 3 4
| from typing import Any
def foo() -> Any: pass
|
泛型
假设有一个函数,要求它既能够处理字符串,又能够处理数字, 参数的类型不可以混着用(比如 a: int 且 b:str ), 就要使用泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from typing import TypeVar, List
T = TypeVar('T', str, int)
def bar(a: T, b: T) -> List[T]: return [a, b]
bar('Joe', 19)
bar(19, 21)
bar('Joe', 'David')
|
- 定义两个泛型 K 和 V,对它两的类型没有做任何限制,也就是说可以是任意类型。
- 函数 get_item() 接受两个参数。
- 这个函数不关心参数 container 字典的键是什么类型,或者字典的值是什么类型;
- 但它的参数 container 必须是字典,参数 key 必须与字典的键为同类型,并且返回值和字典的值必须为同类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from typing import Dict, TypeVar
K = TypeVar("K") V = TypeVar("V")
def get_item(key: K, container: Dict[K, V]) -> V: return container[key]
dict_1 = {"age": 10} dict_2 = {99: "dusai"}
print(get_item("age", dict_1))
print(get_item(99, dict_2))
print(get_item("name", dict_2))
|
...
在 Python 中一切皆对象, ...
也是一个对象
...
在 Python 中叫 Ellipsis
1 2 3
| print(...) print(type(...)) print(Ellipsis == ...)
|
类型提示
1 2 3 4
| from typing import Callable, Tuple
Callable[..., int] Tuple[int, ...]
|
函数内部, 相当于 pass
1 2
| def foo1(): pass def foo2(): ...
|
目前正流行开来的高性能 Web 框架 FastAPI 中,也应用了 Ellipsis。它用以表示参数是必填项,这在 Swagger 页面更能直观体现。
Query 对象的第一个参数是默认值 None,请求的时候可以将其省略.
1 2 3 4 5
| async def something(q: str = Query(None, min_length=10)):
async def something(q: str = Query(..., min_length=10)):
|
numpy 中的索引
1 2 3 4 5
| import numpy as np
arr = np.random.random((2,2,2)) print(arr) print(arr[..., 0, 0])
|