Python - 型別提示



Python 的型別提示在 PEP 484 中引入,旨在將靜態型別的優勢帶入動態型別語言。雖然型別提示不會在執行時強制執行型別檢查,但它們提供了一種指定變數、函式引數和返回值的預期型別的方法,這些型別可以透過靜態分析工具(例如mypy)進行檢查。這增強了程式碼的可讀性,方便了除錯,並提高了程式碼的整體可維護性。

Python 中的型別提示使用註解來指定函式引數、返回值和變數賦值。

Python 的型別提示可用於指定各種型別,例如基本資料型別、集合、複雜型別和自定義使用者定義型別。typing 模組提供了許多內建型別來表示這些各種型別:

讓我們逐一詳細瞭解每一個。

基本資料型別

在 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 模組來指定集合型別。

示例

以下是型別提示中使用集合的示例:

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 中,可選型別用於指示變數可以是指定型別或 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 使用聯合型別允許變數接受不同型別的值。當函式或資料結構可以使用各種型別的輸入或產生不同型別的輸出時,這很有用。

示例

以下是這個示例:

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

Any 型別

在 Python 中,Any 型別是一種特殊的型別提示,它表示變數可以是任何型別。它實際上停用了該特定變數或表示式的型別檢查。當事先不知道值的型別或處理動態資料時,這可能很有用。

示例

以下是型別提示中使用 Any 型別的示例:

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 中的類型別名用於為現有型別提供替代名稱。它們可以透過為複雜的型別註解或型別的組合提供清晰的名稱來使程式碼更易於閱讀。這在處理巢狀結構或長型別提示時尤其有用。

示例

以下是型別提示中使用類型別名的示例:

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 模組中的一個函式,它允許我們建立從現有型別派生的 distinct 型別。這對於透過區分相同底層型別的不同用途來增強程式碼型別安全性非常有用。例如,我們可能希望區分使用者 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
廣告