提升代码质量和可读性:学习使用Python的typing库

🍀 前言

博客地址:

👋 简介

在 Python 中,typing 模块是用于类型提示(type hints)的标准库。类型提示是一种在函数参数、返回值和变量上添加类型信息的注解,用于提高代码的可读性、维护性,并支持静态类型检查工具的使用。typing 模块提供了一组工具,用于定义和操作类型提示。

📖 正文

1 typing的优点

使用 typing 模块进行类型提示带来了一些重要的优点,尤其在大型项目中或者团队协作时。以下是一些使用 typing 的优点:

  • **代码可读性提高:**类型提示可以让代码更易读,特别是在函数参数、返回值和变量上添加类型信息,使得代码的意图更加清晰;
  • **提高维护性:**类型提示有助于开发者更容易理解代码的结构和预期输入输出,从而提高代码的维护性。在维护和修改代码时,类型提示可以作为文档帮助开发者理解代码的设计;
  • **静态类型检查:**类型提示允许使用静态类型检查工具,例如 mypy。这类工具可以在编译阶段或运行前检查代码中的类型错误,帮助开发者在早期捕获潜在的问题;
  • **提高编辑器支持:**带有类型提示的代码能够在代码编辑器中获得更好的自动补全和代码提示,提高开发效率。编辑器能够根据类型信息提供更准确的建议;
  • **更好的文档生成:**类型提示可以用于生成文档,例如使用工具如 Sphinx 和 Read the Docs。这使得自动生成的文档更加清晰和准确;
  • **代码可维护性:**在大型项目中,类型提示可以帮助团队协作,减少团队成员之间的沟通成本。类型信息可以作为契约,定义了函数的输入和输出,使得团队成员更容易理解如何正确地使用代码;
  • **更好的错误检测:**静态类型检查工具可以帮助发现潜在的类型错误,提高代码的质量。这使得在运行时之前就能够识别和修复一些常见的错误。

2 基本类型

2.1 字符串类型

字符串通过str或者typing模块中的Text来标准,参数传入一个字符串,返回一个字符串

def type_string(content: str) -> str:
    return content

等价以下方式

from typing import Text

def type_string(content: Text) -> Text:
    return content

在编辑器中只需要在变量后面使用.就会生成字符串可使用的方法
image.png

2.2 数字类型

使用int标注参数为整形,返回值用float标注浮点型。

def type_number(num: int) -> float:
    return num * 1.0
2.3 布尔类型

使用bool标注参数和返回值,进行取反操作。

def type_bool(flag: bool) -> bool:
    return not flag
2.4 空值类型

如果一个方法,没有返回值,我们就可以通过None来标注。

def type_none() -> None:
    print('hello python')
2.5 Any

表示任意类型,比如一个列表,不需要限定类型,可以存储任意类型的元素,可以使用Any来指定。

from typing import Any

data = [1, 'a', 3.14, True
    , [1, 2], {'a': 1, 'b': 2}
    , (1, 2, 3)
    , {1, 2, 3}]

def type_any(lst: Any) -> None:
    for i in lst:
        print(f"{i}的类型为:{type(i)}")

type_any(data)

# 1的类型为:<class 'int'>
# a的类型为:<class 'str'>
# 3.14的类型为:<class 'float'>
# True的类型为:<class 'bool'>
# [1, 2]的类型为:<class 'list'>
# {'a': 1, 'b': 2}的类型为:<class 'dict'>
# (1, 2, 3)的类型为:<class 'tuple'>
# {1, 2, 3}的类型为:<class 'set'>

总结来说,当一个方法的参数或者返回值的类型不确定或者比较多的时候,我们就可以使用Any来指定。

2.6 Union

表示多个类型中的一个,比如一个计算一个数的平方,这个数可以是整形也可以是浮点型。

from typing import Union

def square_root(n: Union[int, float]) -> Union[int, float]:
    return n ** 2

# 参数为整数
print(square_root(5))
# 参数为浮点数
print(square_root(2.5))

3 集合类型

3.1 列表类型

使用list标注参数为列表类型,对列表中元素进行求平方,返回值用list标注。

nums = [1, 2, 3, 4, 5]
def type_list(data: list) -> list:
    return [i ** 2 for i in data]

print(type_list(nums))

# [1, 4, 9, 16, 25]

通过typing模块中的List,我们可以将参数的类型标注的更加明确,实现对列表元素求平方,并将结果转换成字符串列表返回:

from typing import List

nums = [1, 2, 3, 4, 5]
def type_list(data: List[int]) -> List[str]:
    return [str(i ** 2) for i in data]

print(type_list(nums))
# ['1', '4', '9', '16', '25']
3.2 字典类型

使用dict标注参数类型和返回值类型,实现对成绩求平均值,并添加到原字典中

score = {
    'chinese': 80,
    'math': 95,
    'english': 88
}

def type_dict(d: dict) -> dict:
    d['avg'] = int(sum(v for v in d.values()) / len(d))
    return d

print(type_dict(score))

# {'chinese': 80, 'math': 95, 'english': 88, 'avg': 87}

通过typing模块中的Dict,我们可以将参数的类型标注的更加明确:

from typing import Dict

def type_dict(d: Dict[str, int]) -> Dict[str, int]:
    d['avg'] = int(sum(v for v in d.values()) / len(d))
    return d
print(type_dict(score))

# {'chinese': 80, 'math': 95, 'english': 88, 'avg': 87}
3.3 集合类型

使用set标注参数类型和返回值类型,给集合添加元素,返回集合:

nums = {1, 2, 3}

def type_set(s: set) -> set:
    s.add(100)
    return s

print(type_set(nums))

# {1, 2, 3, 100}

通过typing模块中的Set,我们可以将参数的类型标注的更加明确,并且将集合的元素转换成字符串返回:

from typing import Set

nums = {1, 2, 3}

def type_set(s: Set[int]) -> Set[str]:
    s.add(100)
    return set(str(i) for i in s)

print(type_set(nums))

# {'1', '2', '100', '3'}
3.4 元组类型

通过tuple或者typing模块中Tuple来指定类型,用法与字典,列表,集合一样,我们将列表转换成元组

from typing import List, Tuple

data = ['a', 'b', 'c']

def type_tuple(lst: List[str]) -> Tuple[str]:
    return tuple(lst)

print(type_tuple(data))

# ('a', 'b', 'c')

4 别名

4.1 类型别名

用于为复杂的类型注解创建简洁的别名,提高代码的可读性和可维护性。

from typing import List, Union

# 定义类型别名
IntOrFloat = Union[int, float]
Vector = List[IntOrFloat]

def square_root(n: IntOrFloat) -> Vector:
    return [n ** 1, n ** 2, n ** 3]

print(square_root(5))
4.2 NewType

用于创建新的类型,它可以帮助我们为现有类型创建更具表达性的别名。

from typing import NewType

# 创建新类型别名
UserId = NewType('UserId', int)
Username = NewType('Username', str)

def print_user(user_id: UserId, username: Username) -> None:
    print(f"User Info: ID={user_id}, Name={username}")

user_id = UserId(123)
username = Username("张三")
print_user(user_id, username)

# User Info: ID=123, Name=张三

5 泛型

5.1 TypeVar

用于创建类型变量(Type Variable)。类型变量表示一个未知的类型,用于在泛型类型注解中引入灵活性。

from typing import TypeVar, List

T = TypeVar('T')

def reverse_list(lst: List[T]) -> List[T]:
    return lst[::-1]

result = reverse_list([1, 2, 3, 4])
print(result)

# [4, 3, 2, 1]
5.2 Generic

用于创建泛型类型注解,即可以适用于多种类型的注解。它通常与其他类型注解一起使用,例如List, Tuple, Dict等。

from typing import TypeVar, Generic, List

T = TypeVar('T')

# 定义一个泛型类
class Stack(Generic[T]):
    def __init__(self):
        self.items: List[T] = []

    def push(self, item: T) -> None:
        self.items.append(item)

    def pop(self) -> T:
        return self.items.pop()


# 调用泛型类
stack = Stack[int]()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())
5.3 Type

获取类型的类型对象。它通常与泛型类型注解一起使用,用于获取泛型类型的具体类型。

from typing import Type, TypeVar

T = TypeVar('T')

class MyClass:
    pass

def create_instance(cls: Type[T]) -> T:
    return cls()

# 调用函数
my_instance = create_instance(MyClass)
print(type(my_instance))

# <class '__main__.MyClass'>

6 其他类型

6.1 Iterable

表示可迭代对象,即可以使用迭代器进行遍历的对象,例如列表、元组、集合、字典等。

from typing import Iterable


def print_items(items: Iterable[str]) -> None:
    for item in items:
        print(item)

# 传递列表作为参数
my_list = ['apple', 'banana', 'cherry']
print_items(my_list)

# 传递集合作为参数
my_set = {'apple', 'banana', 'cherry'}
print_items(my_set)
6.2 Callable

表示可调用对象的类型注解,包括函数、方法、类等。

from typing import Callable

def apply_operation(operation: Callable[[int, int], int], a: int, b: int) -> int:
    return operation(a, b)

# 定义一个加法函数
def add(x: int, y: int) -> int:
    return x + y

result = apply_operation(add, 3, 4)
print(result)

上述代码中,参数operation使用Callable[[int, int], int]来指定类型,表示这个参数接收一个方法,方法中有两个int类型的参数,并且返回值也为int类型。

6.3 Generator

表示生成器函数的类型注解。生成器函数是一种特殊的函数,可以使用yield语句来生成值,而不是使用return语句

from typing import Generator

def generator_num(n: int) -> Generator[int, None, None]:
    while n > 0:
        yield n
        n -= 1

# 调用countdown函数,获取生成的倒计数序列
cd = generator_num(5)
for i in cd:
    print(i)

# 5
# 4
# 3
# 2
# 1
6.4 Optional

用于表示可选类型的类型注解,即一个参数或变量可以是某种类型,也可以是None。它通常与其他类型注解一起使用,例如Union

from typing import Optional

def type_optional(text: Optional[Union[int, str]]) -> str:
    if text is not None:
        return f"Hello, {text}!"
    else:
        return "Hello, world!"

# 调用函数,传递不同的参数
print(type_optional("abc"))
print(type_optional(123))
print(type_optional(None))

# Hello, abc!
# Hello, 123!
# Hello, world!

上述代码中类型Optional[Union[int, str]]实际上就等价于Union[int, str, None]

6.5 Literal

表示参数或变量的字面值类型注解。它可以用来限定参数或变量只能取特定的字面值。

from typing import Literal

def get_day_of_week(day: Literal["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]) -> str:
    return f"Today is {day}!"

print(get_day_of_week("Monday"))
print(get_day_of_week("Friday"))
print(get_day_of_week("Sunday"))  # 这里会在静态类型检查时报错

虽然打印Sunday的时候会正常打印,但是在编辑器中,会出现代码错误提示。

6.6 Mapping

用于表示键值对的映射,类似于字典类型。它是一个抽象基类,用于表示支持__getitem__和__iter__方法的映射类型。

from typing import Mapping

def print_mapping(data: Mapping[str, int]) -> None:
    for key, value in data.items():
        print(f"Key: {key}, Value: {value}")

data = {"a": 1, "b": 2, "c": 3}
print_mapping(data)

# Key: a, Value: 1
# Key: b, Value: 2
# Key: c, Value: 3
6.7 Sequence

用于表示序列类型,例如列表、元组、字符串等。它是一个抽象基类,用于表示支持__getitem__、__len__和__iter__方法的序列类型。

from typing import Sequence

def print_sequence(data: Sequence[int]) -> None:
    for item in data:
        print(item)

data = [1, 2, 3, 4, 5]
print_sequence(data)

# 1
# 2
# 3
# 4
# 5

当不需要严格区分一个变量或参数到底是列表 list 类型还是元组 tuple 类型,可以使用Sequence类型。

✏ 总结

在函数参数、返回值和变量上添加类型信息,不仅使得代码的意图更加清晰,同时还能使代码在编辑器中获得更好的自动补全和代码提示,提高开发效率,并且能在静态期间发现潜在的类型错误。

💖 欢迎关注我的公众号

在这里插入图片描述

  • 51
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值