python

转自 Python -类型提示 Type Hints - 小菠萝测试笔记 - 博客园 (cnblogs.com)

为什么会有类型提示

  • Python是一种动态类型语言,这意味着我们在编写代码的时候更为自由,运行时不需要指定变量类型
  • 但是与此同时 IDE 无法像静态类型语言那样分析代码,及时给我们相应的提示,比如字符串的 split 方法
def split_str(s):
    strs = s.split(",")

由于不知道参数 s 是什么类型,所以当你敲 s. 的时候不会出现 split 的语法提示

常用类型提示

typing 是在 python 3.5 才有的模块

常用类型提示

  • int,long,float : 整型,长整形,浮点型;
  • bool,str : 布尔型,字符串类型;
  • List, Tuple, Dict, Set :列表,元组,字典, 集合;
  • Iterable,Iterator :可迭代类型,迭代器类型;
  • Generator:生成器类型;

前两行小写的不需要 import,后面三行都需要通过 typing 模块 import 哦

例子

指定函数参数类型

单个参数

# name 参数类型为 str 
def greeting(name: str) :    
	return "hello"

多个参数

# 多个参数,参数类型均不同 
def add(a: int, string: str, f: float, b: bool or str):
	print(a, string, f, b)

bool or str:代表参数 b 可以是布尔类型,也可以是字符串

指定函数返回的参数类型

# 函数返回值指定为字符串 
def greeting(name: str) -> str:
	return "hello"

复杂一点的栗子

from typing import Tuple, List, Dict

# 返回一个 Tuple 类型的数据,第一个元素是 List,第二个元素是 Tuple,第三个元素是 Dict,第四个元素可以是字符串或布尔
def add(a: int, string: str, f: float, b: bool or str) -> Tuple[List, Tuple, Dict, str or bool]:
    list1 = list(range(a))
    tup = (string, string, string)
    d = {"a": f}
    bl = b
    return list1, tup, d, bl


# 不 warn 的调用写法
print(add(1, "2", 123, True))


# 输出结果
([0], ('2', '2', '2'), {'a': 123}, True)

那指定类型的时候用 list、set、dict、tuple 可不可以呢?

可以是可以,但是不能指定里面元素数据类型

def test(a: list, b: dict, c: set, d: tuple):
    print(a, b, c, d)

List[T]、Set[T]

只能传一个类型,传多个会报错

a: List[int, str] = [12,"34"]
b: Set[int, str] = {12,"23"}

print(a)
print(b)

换成

a: List[int] = [12,"34"]
b: Set[int] = {12,"23"}

print(a)
print(b)

Dict[T,T]

e: Dict[str, int] = {"as": 123, "643": 34}
print(e)

Tuple[T]

可以传入多个

d: Tuple[int, str] = (1, "2")
print(d)


# 输出结果
(1, '2')

只写一个int,但是赋值两个int

from typing import Tuple, List, Dict, Set

d: Tuple[int, str] = (1)
print(d)

只写一个 int,赋值两个 int 元素会报 warning

写了两个int,但是赋值多于两个

d: Tuple[int, str] = (1, "2", "2") 

不会报错,但是也会有 warning

指定一个类型,对所有元素生效

d: Tuple[int, ...] = (1, 2, 3)
d: Tuple[Dict[str, str], ...] = ({"name": "poloyy"}, {"age": "33"})

类型别名

可以将复杂一点类型给个别名,这样好用一些

变量例子

# 别名
vector = List[float]

var: vector = [1.1, 2.2]
# 等价写法
var: List[float] = [1.1, 2.2]

函数例子

# float 组成的列表别名
vector_list_es = List[float]
# 字典别名
vector_dict = Dict[str, vector_list_es]
# 字典组成列表别名
vector_list = List[vector_dict]

# vector_list 等价写法,不用别名的话,有点像套娃
vector = List[Dict[str, List[float]]]

# 函数
def scale(scalar: float, vector: vector_list) -> vector_list:
    for item in vector:
        for key, value in item.items():
            item[key] = [scalar * num for num in value]
    print(vector)
    return vector


scale(2.2, [{"a": [1, 2, 3]}, {"b": [4, 5, 6]}])


# 输出结果
[{'a': [2.2, 4.4, 6.6000000000000005]}, {'b': [8.8, 11.0, 13.200000000000001]}]

NewType

可以自定义创一个新类型

  • 主要用于类型检查
  • NewType(name, tp) 返回一个函数,这个函数返回其原本的值
  • 静态类型检查器会将新类型看作是原始类型的一个子类
  • tp 就是原始类型
from typing import NewType

UserId = NewType('UserId123', int)


def name_by_id(user_id: UserId) -> str:
    print(user_id)
    return str(user_id)


UserId('user')  # Fails type check
num = UserId(5)  # type: int

name_by_id(42)  # Fails type check
name_by_id(UserId(42))  # OK

print(type(UserId(5)))

可以看到 UserId 其实也是 int 类型

Callable

是一个可调用对象类型

查看对象是否可调用

# 返回True或False
isinstance(对象, Callable) 

例子

# 最简单的函数
def print_name(name: str):
    print(name)

# 判断函数是否可调用
print(isinstance(print_name, Callable))

x = 1
print(isinstance(x, Callable))

# 输出结果
True
False

函数是可调用的,所以是 True,而变量不是可调用对象,所以是 False

Callable 作为函数返回值

# Callable  作为函数返回值使用,其实只是做一个类型检查的作用,看看返回值是否为可调用对象
from typing import NewType,Callable

def print_name(name: str):
    print(name)
    
def get_name_return() -> Callable[[str], None]:
    return print_name


vars = get_name_return()
vars("test")


# 等价写法,相当于直接返回一个函数对象
def get_name_test():
    return print_name


vars2 = get_name_test()
vars2("test")

TypeVar 泛型

任意类型

# 可以是任意类型
T = TypeVar('T')


def test(name: T) -> T:
    print(name)
    return name


test(11)
test("aa")


# 输出结果
11
aa

指定类型

# 可以是 int,也可以是 str 类型
AA = TypeVar('AA', int, str)

num1: AA = 1
num2: AA = "123"
print(num1, num2)

num3: AA = []


# 输出结果
1 123

Any Type

  • 一种特殊的类型是 Any
  • 静态类型检查器会将每种类型都视为与 Any 兼容,将 Any 视为与每种类型兼容
# Any
from typing import Any

a = None  # type: Any
a1 = []  # OK
a2 = 2  # OK

s = ''  # type: str
s1 = a  # OK


def foo(item: Any) -> int:
    # Typechecks; 'item' 可以是任意类型
    print(item)
    return 1


foo(a)
foo(a1)
foo(a2)
foo(s)
foo(s1)

隐式使用 Any

def legacy_parser(text):
    ...
    return data

# 上述写法等价于下述写法
# 所有没有返回类型或参数类型的函数将隐式默认使用 Any
def legacy_parser(text: Any) -> Any:
    ...
    return data

Union

联合类型

Union[int, str] 表示既可以是 int,也可以是 str

from typing import Union

# vars 变量可以是int也可以是 str 类型
vars: Union[int,str]
vars = 1
print(vars)

vars = "123"
print(vars)

# 赋值列表会有warning
vars = []
print(vars)

等价写法

vars: Union[int, str]
# 等价于
vars: [int or str]


vars: Union[int]
# 等价于
vars: int

union 等价写法

Union[int] == int 

# 最终 Union[int] 返回的也是 int 类型
Union[int, str, int] == Union[int, str]
# 重复的类型参数会自动忽略掉
Union[int, str] == Union[str, int]
# 自动忽略类型参数顺序
Union[Union[int, str], float] == Union[int, str, float]
# union 嵌套 union 会自动解包

Optional

可选类型

和默认参数有什么不一样

  • 官方原话:可选参数具有默认值,具有默认值的可选参数不需要在其类型批注上使用 Optional,因为它是可选的
  • 不过 Optional 和默认参数其实没啥实质上的区别,只是写法不同
  • 使用 Optional 是为了让 IDE 识别到该参数有一个类型提示,可以传指定的类型和 None,且参数是可选非必传的
# 可选参数
def foo(arg: int = 0) -> None:
    ...

# 不传 arg 默认取 0
foo()
  • Optional[int] 等价于 Union[int, None]
  • 意味着:既可以传指定的类型 int,也可以传 None

例子

使用 Optional

def foo_func(arg: Optional[int] = None):
    print(arg)


foo_func()
foo_func(1)


# 输出结果
None
1

使用默认参数的写法

def foo_func(arg: int = None):
    print(arg)


foo_func()
foo_func(1)


# 输出结果
None
1

这种写法,Pycharm 并不会 warning

重点

Optional[] 里面只能写一个数据类型

# 正确
Optional[str]
Optional[List[str]]
Optional[Dict[str, Any]]

# 错误
Optional[str, int]
Optional[Union[str, int, float]]
原文地址:https://www.cnblogs.com/dongye95/p/15427512.html