Python - 类型提示



PEP 484 中引入了 Python 类型提示,以将静态类型的优势引入动态类型语言。尽管类型提示在运行时不强制执行类型检查,但它们提供了一种指定预期类型的变量、函数参数和返回值的方法,这些类型可以通过静态分析工具(如 mypy)进行检查。这增强了代码的可读性,便于调试,并提高了代码的整体可维护性。

Python 中的类型提示使用函数参数、返回值和变量赋值的注释。

Python 的类型提示可用于指定各种类型,例如基本数据类型、集合、复杂类型和自定义用户定义类型。typing 模块提供了许多内置类型来表示这些不同的类型 -

  • 基本数据类型
  • 集合类型
  • 可选类型
  • 联合类型
  • 任何类型
  • 类型别名
  • 泛型类型
  • 可调用类型
  • 文本类型
  • NewType (新类型)

让我们一个接一个地详细看看每一个。

基本数据类型

在 Python 中,当使用类型提示指定基本类型时,我们可以简单地使用类型的名称作为注释。

以下是使用基本数据类型(如整数、浮点数、字符串等)的示例 -


from typing import Optional

# Integer type
def calculate_square_area(side_length: int) -> int:
	 	return side_length ** 2

# Float type
def calculate_circle_area(radius: float) -> float:
	 	return 3.14 * radius * radius

# String type
def greet(name: str) -> str:
	 	return f"Hello, {name}"

# Boolean type
def is_adult(age: int) -> bool:
	 	return age >= 18

# None type
def no_return_example() -> None:
	 	print("This function does not return anything")

# Optional type (Union of int or None)
def safe_divide(x: int, y: Optional[int]) -> Optional[float]:
	 	if y is None or y == 0:
	 	 	 return None
	 	else:
	 	 	 return x / y

# Example usage
print(calculate_square_area(5)) 	 	 	 	
print(calculate_circle_area(3.0)) 	 		
print(greet("Alice")) 	 	 	 	 	 	 	 		
print(is_adult(22)) 	 	 	 	 	 	 	 	 		
no_return_example() 	 	 	 	 	 	 	 	 		
print(safe_divide(10, 2)) 	 	 	 	 	 		
print(safe_divide(10, 0)) 	 	 	 	 	 		
print(safe_divide(10, None)) 	 	 	 	 	

在执行上述代码时,我们将得到以下输出 -

25
28.259999999999998
Hello, Alice
True
This function does not return anything
5.0
None
None

集合类型

在 Python 中,当在类型提示中处理列表元组字典等集合时,我们通常使用 typing 模块来指定集合类型。

下面是使用 in 类型提示的 Collections 的示例 -


from typing import List, Tuple, Dict, Set, Iterable, Generator

# List of integers
def process_numbers(numbers: List[int]) -> List[int]:
	 	return [num * 2 for num in numbers]

# Tuple of floats
def coordinates() -> Tuple[float, float]:
	 	return (3.0, 4.0)

# Dictionary with string keys and integer values
def frequency_count(items: List[str]) -> Dict[str, int]:
	 	freq = {}
	 	for item in items:
	 	 	 freq[item] = freq.get(item, 0) + 1
	 	return freq

# Set of unique characters in a string
def unique_characters(word: str) -> Set[str]:
	 	return set(word)

# Iterable of integers
def print_items(items: Iterable[int]) -> None:
	 	for item in items:
	 	 	 print(item)

# Generator yielding squares of integers up to n
def squares(n: int) -> Generator[int, None, None]:
	 	for i in range(n):
	 	 	 yield i * i

# Example usage
numbers = [1, 2, 3, 4, 5]
print(process_numbers(numbers)) 	 	 	 	 	 	 	 	 		

print(coordinates()) 	 	 	 	 	 	 	 	 	 	 	 	 	 	

items = ["apple", "banana", "apple", "orange"]
print(frequency_count(items)) 	 	 	 	 	 	 	 	 	 	

word = "hello"
print(unique_characters(word)) 	 	 	 	 	 	 	 	 		

print_items(range(5)) 	 	 	 	 	 	 	 	 	 	 	 	 		

gen = squares(5)
print(list(gen)) 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	

在执行上述代码时,我们将得到以下输出 -

[2, 4, 6, 8, 10]
(3.0, 4.0)
{'apple': 2, 'banana': 1, 'orange': 1}
{'l', 'e', 'h', 'o'}
0
1
2
3
4
[0, 1, 4, 9, 16]

可选类型

在 Python 中,Optional 类型用于指示变量可以是指定类型或 None。当函数可能并不总是返回值,或者当参数可以接受值或未指定时,这特别有用。

以下是在类型提示中使用可选类型的示例 -


from typing import Optional

def divide(a: float, b: float) -> Optional[float]:
	 	if b == 0:
	 	 	 return None
	 	else:
	 	 	 return a / b

result1: Optional[float] = divide(10.0, 2.0) 	 # result1 will be 5.0
result2: Optional[float] = divide(10.0, 0.0) 	 # result2 will be None

print(result1) 	
print(result2) 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 		

在执行上述代码时,我们将得到以下输出 -

5.0
None

联合类型

Python 使用 Union 类型来允许变量接受不同类型的值。当函数或数据结构可以处理各种类型的输入或生成不同类型的输出时,这非常有用。

下面是这个例子 -


from typing import Union

def square_root_or_none(number: Union[int, float]) -> Union[float, None]:
	 	if number >= 0:
	 	 	 return number ** 0.5
	 	else:
	 	 	 return None

result1: Union[float, None] = square_root_or_none(50) 		
result2: Union[float, None] = square_root_or_none(-50) 	

print(result1) 	
print(result2) 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 		

在执行上述代码时,我们将得到以下输出 -

7.0710678118654755
None

任何类型

在 Python 中,Any type 是一种特殊的类型提示,指示变量可以是任何类型。它实质上禁用了该特定变量或表达式的类型检查。这在事先不知道值类型或处理动态数据的情况下非常有用。

以下是在 Type hint 中使用 Any type 的示例 -


from typing import Any

def print_value(value: Any) -> None:
	 	print(value)

print_value(10) 	 	 	 		
print_value("hello") 	 	
print_value(True) 	 	 		
print_value([1, 2, 3]) 	
print_value({'key': 'value'}) 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 		

在执行上述代码时,我们将得到以下输出 -

10
hello
True
[1, 2, 3]
{'key': 'value'}

类型别名

Python 中的类型别名用于为现有类型提供替代名称。它们可以通过为复杂的类型注释或类型组合提供清晰的名称来使代码更易于阅读。这在使用嵌套结构或长类型提示时特别有用。

以下是在 Type hint 中使用 Type Aliases 的示例 -


from typing import List, Tuple

# Define a type alias for a list of integers
Vector = List[int]

# Define a type alias for a tuple of coordinates
Coordinates = Tuple[float, float]

# Function using the type aliases
def scale_vector(vector: Vector, factor: float) -> Vector:
	 	 return [int(num * factor) for num in vector]

def calculate_distance(coord1: Coordinates, coord2: Coordinates) -> float:
	 	x1, y1 = coord1
	 	x2, y2 = coord2
	 	return ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5

# Using the type aliases
v: Vector = [1, 2, 3, 4]
scaled_v: Vector = scale_vector(v, 2.5)
print(scaled_v) 	

c1: Coordinates = (3.0, 4.0)
c2: Coordinates = (6.0, 8.0)
distance: float = calculate_distance(c1, c2)
print(distance) 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 		

在执行上述代码时,我们将得到以下输出 -

[2, 5, 7, 10]
5.0

泛型类型

泛型类型创建可以处理任何类型的函数、类或数据结构,同时保持类型安全。typing 模块的 TypeVar 和 Generic 结构使这成为可能。它们有助于制作可重用的组件,这些组件可以在不影响类型检查的情况下处理各种类型的组件。

这是它的示例 -


from typing import TypeVar, List

# Define a type variable T
T = TypeVar('T')

# Generic function that returns the first element of a list
def first_element(items: List[T]) -> T:
	 	return items[0]

# Example usage
int_list = [1, 2, 3, 4, 5]
str_list = ["apple", "banana", "cherry"]

first_int = first_element(int_list) 	 	 	# first_int will be of type int
first_str = first_element(str_list) 	 	 	# first_str will be of type str

print(first_int) 	 	
print(first_str) 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	

在执行上述代码时,我们将得到以下输出 -

1
apple

可调用类型

Python 的 Callable 类型用于显示类型是函数或可调用对象。它位于 typing 模块中,可让您定义参数的类型和函数的返回类型。这对于高阶函数很方便。

以下是在类型提示中使用 Callable 类型的示例 -


from typing import Callable

# Define a function that takes another function as an argument
def apply_operation(x: int, y: int, operation: Callable[[int, int], int]) -> int:
	 	return operation(x, y)

# Example functions to be passed as arguments
def add(a: int, b: int) -> int:
	 	return a + b

def multiply(a: int, b: int) -> int:
	 	return a * b

# Using the apply_operation function with different operations
result1 = apply_operation(5, 3, add) 	 	 	 	# result1 will be 8
result2 = apply_operation(5, 3, multiply) 	 # result2 will be 15

print(result1) 	
print(result2) 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	

在执行上述代码时,我们将得到以下输出 -

8
15

文本类型

Literal 类型用于指定值必须恰好是一组预定义值之一。

下面是示例 -


from typing import Literal

def move(direction: Literal["left", "right", "up", "down"]) -> None:
	 	print(f"Moving {direction}")

move("left") 	# Valid
move("up") 	 	# Valid 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 		

在执行上述代码时,我们将得到以下输出 -

Moving left
Moving up

NewType (新类型)

NewType 是 typing 模块中的一个函数,它允许我们创建从现有类型派生的不同类型。这对于通过区分同一底层类型的不同用途来为代码添加类型安全非常有用。例如,我们可能希望区分用户 ID 和产品 ID,即使两者都表示为整数。

下面是示例 -


from typing import NewType

# Create new types
UserId = NewType('UserId', int)
ProductId = NewType('ProductId', int)

# Define functions that use the new types
def get_user_name(user_id: UserId) -> str:
	 	return f"User with ID {user_id}"

def get_product_name(product_id: ProductId) -> str:
	 	return f"Product with ID {product_id}"

# Example usage
user_id = UserId(42)
product_id = ProductId(101)

print(get_user_name(user_id)) 	 # Output: User with ID 42
print(get_product_name(product_id)) 	# Output: Product with ID 101 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 		

在执行上述代码时,我们将得到以下输出 -

User with ID 42
Product with ID 101