前言:
Python的typing模块自Python 3.5开始引入,提供了类型系统的扩展,能够帮助程序员定义变量、函数的参数和返回值类型等。这使得代码更易于理解和检查,也方便了IDE和一些工具进行类型检查,提升了代码的质量。
typing 模块概述
typing 模块是 Python 标准库中用于支持类型提示的模块。它提供了一系列的类型和类型相关的工具,帮助开发者在代码中添加类型提示,以提高代码的可读性和可靠性。
1 类型提示的基本使用
1.1 变量类型注解:
age: int = 25
name: str = "Alice"
1.2函数参数和返回值类型注解:
def greet(name: str) -> str:
return "Hello, " + name
1.3列表、字典等复杂类型注解:
from typing import List, Dict
numbers: List[int] = [1, 2, 3]
user_info: Dict[str, str] = {"name": "Alice", "age": "25"}
2 常见的typing模块类型
List[T],Set[T],Dict[K, V]等:用于注解容器类型,T、K、V分别代表容器内元素类型,字典的键和值类型。
2.1 Tuple[T, …]:用于定长元组。
from typing import Tuple
coordinate: Tuple[int, int, int] = (10, 20, 30)
2.2 Optional[T]:用于表示某个类型或None。
from typing import Optional
def get_age(name: str) -> Optional[int]:
if name == "Alice":
return 25
return None
2.3 Union[T, U, …]:当一个参数可能是多种类型之一时使用。
from typing import Union
def log_message(message: Union[str, int]) -> None:
print(message)
2.4 Callable[[Arg1Type, Arg2Type], ReturnType]:用于表示可调用对象(如函数)的类型签名。
from typing import Callable
def add(a: int, b: int) -> int:
return a + b
calculator: Callable[[int, int], int] = add
3 Type Hints的高级应用
3.1 泛型:在定义类或函数时,允许使用类型参数。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/17
# @Author : Summer
# @File :
# @describe:
"""
from typing import TypeVar, Generic, List
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self._container: List[T] = []
def push(self, item: T) -> None:
self._container.append(item)
def pop(self) -> T:
return self._container.pop()
stack = Stack[int]()
stack.push(1)
print(stack.pop()) # 输出1
上述代码的解析:首先从typing模块导入TypeVar、Generic和List。这些是实现自定义泛型类型的核心组件。
T = TypeVar(‘T’):使用TypeVar创建一个类型变量T。这个变量将用于表示Stack类可以接受的元素的类型。TypeVar是定义泛型类和函数时的占位符,表示可以是任意类型。
class Stack(Generic[T]):定义了一个名为Stack的类,这个类是泛型的,可以存储类型为T的元素。这里的T是在之前用TypeVar定义的类型变量。Generic[T]表示Stack是一个泛型类,它的行为可以基于不同的类型T进行参数化。
在__init__方法中,初始化了一个名为_container的列表,这个列表是用于存储栈中元素的容器。List[T]表示_container是一个列表,其中可以存储任意类型为T的元素。
stack = Stackint创建了一个Stack的实例stack,指定T为int类型。这意味着这个stack实例是一个整型的栈,只能存储整数。
通过push方法向栈中压入一个整数1。
通过pop方法从栈中弹出最后一个元素,也就是之前压入的整数1。
这段代码展示了如何使用Python的typing模块创建泛型数据结构,使得这个数据结构变得更加通用和灵活。它可以在保持类型安全的同时,被用于存储任何指定类型的数据。
3.2 NewType:
用于定义新类型,以帮助区分具有相同基础类型但应用场景不同的场合。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/17
# @Author : Summer
# @File :
# @describe:
"""
from typing import NewType
UserId = NewType('UserId', int)
def get_user_name(user_id: UserId) -> str:
return "User#" + str(user_id)
user_id = UserId(524313)
print(get_user_name(user_id)) # User#524313
user_id = UserId(524313):使用UserId类型创建一个新的用户ID实例。注意,尽管UserId是基于int的一个新类型,但是这一步看起来和直接对整数进行类型转换(如int(x))相似。然而,UserId(524313)在类型检查时被视为一个与int不同的独立类型。
print(get_user_name(user_id))调用get_user_name函数,并传入之前创建的user_id。然后打印函数的返回值,预期输出为"User#524313"。
4 类型检查工具
typing 模块还提供了一些用于类型检查的工具,如 isinstance()、issubclass() 等。
from typing import List
def is_valid_input(data: List) -> bool:
return isinstance(data, List)
print(is_valid_input(data=1)) # False
print(is_valid_input(data=[1, 2, 3])) # True
5 结构化类型和协议
Python 的 typing 模块还支持结构化类型和协议,用于表示对象的形状或接口。可以使用 Protocol 类来定义一个协议,并在参数类型注解中使用。
举个例子,如果我们想定义一个Writable的协议,所有满足这个协议的对象都应该有一个write方法。这里是如何使用Protocol来做这件事的:
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/17
# @Author : Summer
# @File :
# @describe:
"""
from typing import Protocol
class Writable(Protocol):
def write(self, data: str) -> int:
...
# 实现了 Writable 接口的类
class FileWriter:
def write(self, data: str) -> int:
print(f"Writing {data} to file.")
return len(data) # 假设返回写入的字节数
# 实现了 Writable 接口的类
class StringWriter:
def write(self, data: str) -> int:
print(f"Writing {data} to string buffer.")
return len(data) # 假设返回写入的字节数
# 函数接受任何实现了Writable接口的对象
def write_data(writer: Writable, data: str):
num_bytes_written = writer.write(data)
print(f"Wrote {num_bytes_written} bytes.")
return
# FileWriter和StringWriter没有从Writable显式继承,但它们实现了Writable协议
file_writer = FileWriter()
string_writer = StringWriter()
# 可以传递给write_data函数,因为它们符合Writable协议
write_data(file_writer, "Hello, World!")
write_data(string_writer, "Hello, Python!")
Writable(Protocol)定义了一个需要有write方法的协议,该方法接受一个字符串data并返回一个整数。
类FileWriter和StringWriter都有一个write方法,这意味着它们“符合”Writable协议。注意,我们没有明确地让FileWriter或StringWriter类继承Writable协议。
函数write_data接受Writable类型的参数writer。这意味着任何实现了write方法的对象都可以传递给这个函数。
最后,创建了FileWriter和StringWriter的实例,然后将它们传递给write_data函数,展示了如何在不使用继承的情况下通过协议来实现接口兼容性。
这种方式的好处是可以让你的代码更加灵活,并且它鼓励更好的分离关注点。另外,由于它基于鸭子类型,所以它对于一些动态编程模式也非常有用,例如在mocking或依赖注入中。
小结:
Python的typing模块为静态类型检查提供了强大的支持,通过在代码中添加类型注解,不仅可以提高代码的可读性,还可以利用静态类型检查工具(如mypy)来发现潜在的错误。随着Python生态的发展,类型提示已经成为Python编程的一个重要部分。
如果你觉得文章还不错,请大家 点赞、分享、留言 ,因为这将是我持续输出更多优质文章的最强动力!当然如果有小伙伴需要python、java、性能等等相关的学习资料的也可以私聊我获取哈,都是本人之前收集的一些学习资料,都可以免费提供给大家学习,希望能对大家有帮助。