27.Python 变量类型、返回值类型及`...`

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  # int 变量, 默认值为 0
bool_var: bool = True # bool 变量, 默认值为 True
dict_var: dict = {} # 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 # py 3.8+ 才有的写法

解包

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
# T 必须是 str 或 int 其中一种
T = TypeVar('T', str, int)

def bar(a: T, b: T) -> List[T]:
return [a, b]

# 类型检查不通过
# 函数的参数必须为同一个类型"T"
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 和 V
    # K 和 V 的具体类型没有限制
    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))
    # 例1
    # 类型检查通过,输出: 10

    print(get_item(99, dict_2))
    # 例2
    # 类型检查通过,输出: dusai

    print(get_item("name", dict_2))
    # 例3
    # 类型检查失败
    # 因为"name"是字符串,而dict_2的键为整型

    ...

在Python中一切皆对象, ... 也是一个对象
... 在 Python 中叫 Ellipsis

1
2
3
print(...)    # Ellipsis
print(type(...)) # <class 'ellipsis'>
print(Ellipsis == ...) # True

类型提示

1
2
3
4
from typing import Callable, Tuple

Callable[..., int] # 输入参数随意,返回值为int
Tuple[int, ...] # int组成的元组

函数内部, 相当于 pass

1
2
def foo1(): pass
def foo2(): ...

目前正流行开来的高性能 Web 框架 FastAPI 中,也应用了 Ellipsis。它用以表示参数是必填项,这在 Swagger 页面更能直观体现。

Query 对象的第一个参数是默认值 None,请求的时候可以将其省略.

1
2
3
4
5
# 如果想做一些验证,比如最小长度是 10,并且是可选的,可以写成这样
async def something(q: str = Query(None, min_length=10)):

# 如果想做一些验证,比如最小长度是 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])