Python的类型注解

参考:
Python类型注解,你需要知道的都在这里了


1. 变量注解

Python 是动态语言,其显著特点是在声明变量时,你不需要显式声明它的类型。

比如这个:

age = 20
print('The age is: ', age + 1)
# Output:
# The age is:  21

你看,虽然代码里没有明确指定 age 的类型,但是程序运行时隐式推断出它是 int 类型,因此可以顺利执行 age + 1 的动作。

除此之外,已经确定类型的变量,可以随时更改其类型,比如:

age = 20
print(type(age))
# Output: <class 'int'>

age = '20'
print(type(age))
# Output: <class 'str'>

Python 这种动态特性的好处是它非常的自由,大部分时候你不用纠结类型声明、类型转化等麻烦事,可以用很少的代码完成各种骚操作。但是缺点也在这里:如果你代码某些变量的类型有错,编辑器、IDE等工具无法在早期替你纠错,只能在程序运行阶段才能够暴露问题。

比如下面这个例子:

age = 20

# ...
# 这里进行了一大串的其他指令
# 然后你忘记了 age 应该是 int
# 错误地将其赋值为字符串

age = '20'

print('The age is: ', age + 1)
# Output: TypeError: can only concatenate str (not "int") to str

在项目代码逐渐膨胀之后,上面这种看似弱智的情况可能会经常发生。

因此,Python 3.5 之后引入了类型注解,其作用就是让你可以明确的声明变量的类型,使代码不再那么的自由(放飞自我)。

类型注解还在快速发展中,因此尽量用较新的 Python 版本去尝试它。
比如上面的代码,就可以用类型注解改写了:

age: int = 20

很简单,但却带来了巨大的好处:

  • 编辑器可以替你揪出代码中关于类型的错误,避免了程序运行过程中各种奇奇怪怪的 Bug 。
  • 在你编写代码时,编辑器可以提示你对象的类型,免得你或者团队成员忘记了。(程序员通常记性不好)。

注意,类型注解仅仅是提供给编辑器进行类型检查的机会,也就是起提示的作用,对 Python 程序的运行不会产生任何影响。也就是说,Python 跟以前一样自由,即使你进行了错误的类型赋值,只要不直接引发错误,程序依旧可以运行。

最后,Python 中几种基本的变量类型都得到了支持:

a: int = 3
b: float = 3.14
c: str = 'abc'
d: bool = False

2. 函数注解

如下,是简单的函数类型注解:

def say_hi(name: str) -> str:
    return f'Hello {name}!'

你可以很清楚的知道,这个函数应该接收一个字符串参数 name ,并且返回值应该也是字符串。

带默认值的函数像这样书写:

def add(first: int = 10, second: float = 5.5) -> float:
    return first + second

如果函数没有返回值,那么下面两种写法都可以:

def foo():
    pass

def bar() -> None:
    pass

自定义的对象也没问题,像下面这样:

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


def hello(p: Person) -> str:
    return f'Hello, {p.name}'

如果要避免循环导入或者注解早于对象定义的情况,可以用字符串代替类型:

def hello(p: 'Person') -> str:
    return f'Hello, {p.name}'


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

效果是相同的。

相比变量类型注解,函数里的类型注解更加有用,并且可能是你最频繁用到注解的地方了。

3. 容器类型

列表、字典、元组等包含元素的复合类型,用简单的 list,dict,tuple 不能够明确说明内部元素的具体类型。

因此要用到 typing 模块提供的复合注解功能:

from typing import List, Dict, Tuple

# 参数1: 元素为 int 的列表
# 参数2: 键为字符串,值为 int 的字典
# 返回值: 包含两个元素的元组
def mix(scores: List[int], ages: Dict[str, int]) -> Tuple[int, int]:
    return (0, 0)

如果你用的是 Python 3.9+ 版本,甚至连 typing 模块都不需要了,内置的容器类型就支持了复合注解:

def mix(scores: list[int], ages: dict[str, int]) -> tuple[int, int]:
    return (0, 0)

在某些情况下,不需要严格区分参数到底是列表还是元组(这种情况还蛮多的)。这时候就可以将它们的特征抽象为更泛化的类型(泛型),比如 Sequence(序列)。

下面是例子:

# Python 3.8 之前的版本
from typing import Sequence as Seq1

def foo(seq: Seq1[str]):
    for item in seq:
        print(item)


# Python 3.9+ 也可以这么写
from collections.abc import Sequence as Seq2

def bar(seq: Seq2[str]):
    for item in seq:
        print(item)

例子中函数的参数不对容器的类型做具体要求,只要它是个序列(比如列表和元组)就可以。

  • 13
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吮指原味张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值