11.变量类型、返回值类型及`...`
大约 5 分钟学习笔记Python参数类型
Python 3.6+ 版本
加入了对类型提示
的支持 这些类型提示
是一种新的语法, 用来声明一个变量的类型
没啥用的 _前言_ 废话
- Python 是一种动态类型语言,这意味着我们在编写代码的时候更为自由,运行时不需要指定变量类型
- 但是与此同时 IDE 无法像静态类型语言那样分析代码,及时给我们相应的提示,比如字符串的 split 方法
def split_str(s);
strs = s.split(",")
由于不知道参数 s 是什么类型,所以当你敲 s. 的时候不会出现 split 的语法提示
解决上述问题,类型提示
Python 3.5、3.6 新增了两个特性 PEP 484 和 PEP 526
- 变量提示:PEP 484:https://www.python.org/dev/peps/pep-0484/
- 函数参数提示:PEP 526:https://www.python.org/dev/peps/pep-0526/ 帮助 IDE 为我们提供更智能的提示
这些新特性不会影响语言本身,只是增加一点提示
简单类型
num: int = 0 # int 变量, 默认值为 0
bool_var: bool = True # bool 变量, 默认值为 True
dict_var: dict = {} # dict 变量, 默认值为 空
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
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 类型
from typing import Set, Tuple
def process_items(items_t: Tuple[int, int, str], items_s: Set[bytes]):
return items_t, items_s
元组打包、解包
打包
t1: Tuple[int, ...] = (1,2,3) # 元组加上类型打包
t2: Tuple[int, ...] = 1,2,3 # py 3.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 类型
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
类型
class Person:
def __init__(self, name: str):
self.name = name
def get_person_name(one_person: Person):
return one_person.name
返回值类型
def say_hi(name: str) -> str:
return f'Hello {name}!'
def add(first: int = 10, second: float = 5.5) -> float:
return first + second
如果要避免循环导入或者注解早于对象定义的情况, 可以用字符串代替类型, 效果相同
def hello(p: 'Person') -> str:
return f'Hello, {p.name}'
class Person:
def __init__(self, name: str):
self.name = name
如果你实在不知道某个类型注解应该怎么写时,这里还有个最后的逃生通道 任何类型都与 Any 兼容。当然如果你把所有的类型都注解为 Any 将毫无意义,因此 Any 应当尽量少使用。
from typing import Any
def foo() -> Any:
pass
泛型
假设有一个函数,要求它既能够处理字符串,又能够处理数字, 参数的类型不可以混着用(比如 a: int 且 b:str ), 就要使用泛型
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 必须与字典的键为同类型,并且返回值和字典的值必须为同类型。
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
print(...) # Ellipsis
print(type(...)) # <class 'ellipsis'>
print(Ellipsis == ...) # True
类型提示
from typing import Callable, Tuple
Callable[..., int] # 输入参数随意,返回值为int
Tuple[int, ...] # int组成的元组
函数内部, 相当于 pass
def foo1(): pass
def foo2(): ...
提示
目前正流行开来的高性能 Web 框架 FastAPI 中,也应用了 Ellipsis。它用以表示参数是必填项,这在 Swagger 页面更能直观体现。
Query 对象的第一个参数是默认值 None,请求的时候可以将其省略.
# 如果想做一些验证,比如最小长度是 10,并且是可选的,可以写成这样
async def something(q: str = Query(None, min_length=10)):
# 如果想做一些验证,比如最小长度是 10,并且是必选的,则需要写成这样
async def something(q: str = Query(..., min_length=10)):
numpy 中的索引
import numpy as np
arr = np.random.random((2,2,2))
print(arr)
print(arr[..., 0, 0])