Python 类型提示

网络     2021年07月05日    分类: Python   阅读:4075     评论:0

Python 3.6+ 版本加入了对"类型提示"的支持。

通过声明变量的类型,编辑器和一些工具能给你提供更好的支持。

但类型提示不是强制语法,虽然有警告,但不会中断程序运行。

自动完成

从下面的例子开始:

def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

运行这段程序将输出:

John Doe

这个函数做了下面这些事情:

  • 接收 first_namelast_name 参数。
  • 通过 title() 将每个参数的第一个字母转换为大写形式。
  • 中间用一个空格来拼接它们。

这是一个非常简单的程序。

现在假设你是从头开始编写这段程序。

在某一时刻,你开始定义函数,并且准备好了参数...

现在你需要调用一个"将第一个字母转换为大写形式的方法"。

等等,那个方法是什么来着?upper?还是 uppercasefirst_uppercasecapitalize

然后你尝试向程序员老手的朋友——编辑器自动补全寻求帮助。

输入函数的第一个参数 first_name,输入点号(.)然后敲下 Ctrl+Space 来触发代码补全。

但遗憾的是并没有起什么作用:

img

让我们来修改上面例子的一行代码。

把下面这段代码中的函数参数从:

    first_name, last_name

改成:

    first_name: str, last_name: str

这样就可以了。

这些就是"类型提示":

def get_full_name(first_name: str, last_name: str):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

它和提供参数默认值不一样。

我们用的是冒号(:),不是等号(=)。

而且添加类型提示一般不会改变原来的运行结果。

现在假设我们又一次正在创建这个函数,不同的是这次添加了类型提示。

在同样的地方,通过 Ctrl+Space 触发自动补全,如下图所示:

img

你可以滚动查看选项,直到你找到看起来眼熟的那个:

img

类型检查

下面是一个已经有类型提示的函数:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + age
    return name_with_age

因为编辑器已经知道了这些变量的类型,所以不仅能对代码进行补全,还能检查其中的错误:

img

现在你知道了必须先修复这个问题,通过 str(age)age 转换成字符串:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + str(age)
    return name_with_age

声明类型

你刚刚看到的就是声明类型提示的主要场景,用于函数的参数。

一般类型

可以声明所有的标准 Python 类型,比如:

  • int
  • float
  • bool
  • bytes

嵌套类型

有些容器类数据结构可以包含其他的值,比如 dictlistsettuple。它们内部的值也会拥有自己的类型。

可以使用 Python 的 typing 标准库来声明这些类型以及子类型,它专门用来支持这些类型提示。

列表

例如,让我们来定义一个由 str 组成的 list 变量。

typing 模块导入 List(注意是大写的 L):

from typing import List


def process_items(items: List[str]):
    for item in items:
        print(item)

思考一下,为什么不用list?,比如:

def process_items(items: list): for item in items: print(item)

很显然,这样无法定义列表中的元素的数据类型。

同样以冒号(:)来声明这个变量,输入 List 作为类型。

由于列表是带有"子类型"的类型,所以我们把子类型放在方括号中。

这表示:"变量 items 是一个 list,并且这个列表里的每一个元素都是 str"。

这样,即使在处理列表中的元素时,你的编辑器也可以提供自动完成支持:

img

元组和集合

声明 tupleset 的方法也是一样的:

from typing import Set, Tuple


def process_items(items_t: Tuple[int], items_s: Set[bytes]):
    return items_t, items_s

这表示:

  • 变量 items_t 是一个 tuple,其中的每个元素都是 int 类型。
  • 变量 items_s 是一个 set,其中的每个元素都是 bytes 类型。

字典

定义 dict 时,需要传入两个子类型,用逗号进行分隔。

第一个子类型声明 dict 的所有键的数据类型。

第二个子类型声明 dict 的所有值的数据类型:

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)

这表示变量 prices 是一个 dict。这个 dict 的所有键为 str 类型。这个 dict 的所有值为 float 类型。

类作为类型

也可以将某个类声明为某个变量的类型。

假设你有一个名为 Person 的类,拥有 name 属性:

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

Pydantic 模型

Pydantic 是一个用来用来执行数据校验的 Python 库。

通过它,你可以将数据的"结构"声明为具有属性的类。并且每个属性都可以拥有各自的类型。

不同于上面的Person类。

Pydantic 定义的是数据类型的结构,而不是我们通常认知的类。

你可以用一些初始值来创建这个类的实例,并且:

  • 这些值会被校验类型
  • 值会自动被转换为适当的类型(在需要的情况下)
  • 返回一个包含所有数据的对象。
  • 获得所有编辑器对它的自动完成支持。

下面的例子来自 Pydantic 官方文档:

from datetime import datetime
from typing import List, Optional

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = "John Doe"
    signup_ts: Optional[datetime] = None
    friends: List[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}
user = User(**external_data)

print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]

print(user.id)
# > 123

评论总数: 0