周志洋

个人站

持续学习 才能不被淘汰


Python实用技巧-类型注解详解

类型注解的重要性

Python 3.5引入了类型注解功能,它允许开发者为变量、函数参数和返回值指定类型信息。虽然Python仍然是动态类型语言,但类型注解提供了以下好处:

  • 提高代码可读性:明确表达代码的意图
  • 更好的IDE支持:自动补全、错误检测
  • 静态类型检查:使用mypy等工具提前发现类型错误
  • 文档化:类型信息本身就是很好的文档

基础类型注解

1. 基本类型注解

from typing import List, Dict, Optional, Tuple, Union, Any

def basic_type_annotations():
    """基础类型注解示例"""
    
    # 基本类型
    name: str = "张三"
    age: int = 25
    height: float = 175.5
    is_student: bool = True
    
    # 集合类型
    numbers: List[int] = [1, 2, 3, 4, 5]
    scores: Dict[str, int] = {"数学": 95, "英语": 88, "物理": 92}
    coordinates: Tuple[float, float] = (39.9042, 116.4074)
    
    # 可选类型
    email: Optional[str] = None
    phone: Optional[str] = "13800138000"
    
    # 联合类型
    id_value: Union[int, str] = 123
    id_value = "user_123"
    
    # 任意类型
    data: Any = {"key": "value"}
    
    print(f"姓名: {name}, 类型: {type(name)}")
    print(f"年龄: {age}, 类型: {type(age)}")
    print(f"分数: {scores}")
    print(f"坐标: {coordinates}")

basic_type_annotations()

2. 函数类型注解

from typing import List, Dict, Optional, Tuple, Callable

def function_type_annotations():
    """函数类型注解示例"""
    
    def calculate_average(numbers: List[float]) -> float:
        """计算平均值"""
        if not numbers:
            return 0.0
        return sum(numbers) / len(numbers)
    
    def find_max_value(data: Dict[str, int]) -> Optional[int]:
        """查找最大值"""
        if not data:
            return None
        return max(data.values())
    
    def process_user_data(
        name: str, 
        age: int, 
        email: Optional[str] = None
    ) -> Dict[str, Union[str, int]]:
        """处理用户数据"""
        result = {"name": name, "age": age}
        if email:
            result["email"] = email
        return result
    
    def create_coordinate(x: float, y: float) -> Tuple[float, float]:
        """创建坐标点"""
        return (x, y)
    
    def apply_operation(
        numbers: List[int], 
        operation: Callable[[int], int]
    ) -> List[int]:
        """应用操作到数字列表"""
        return [operation(x) for x in numbers]
    
    # 测试函数
    numbers = [1.5, 2.5, 3.5, 4.5]
    average = calculate_average(numbers)
    print(f"平均值: {average}")
    
    scores = {"数学": 95, "英语": 88, "物理": 92}
    max_score = find_max_value(scores)
    print(f"最高分: {max_score}")
    
    user_data = process_user_data("李四", 30, "lisi@example.com")
    print(f"用户数据: {user_data}")
    
    coord = create_coordinate(39.9042, 116.4074)
    print(f"坐标: {coord}")
    
    def square(x: int) -> int:
        return x * x
    
    squared_numbers = apply_operation([1, 2, 3, 4], square)
    print(f"平方数: {squared_numbers}")

function_type_annotations()

高级类型注解

1. 泛型和类型变量

from typing import TypeVar, Generic, List, Dict, Any

def advanced_type_annotations():
    """高级类型注解示例"""
    
    # 类型变量
    T = TypeVar('T')
    K = TypeVar('K')
    V = TypeVar('V')
    
    class Container(Generic[T]):
        """泛型容器类"""
        def __init__(self, value: T):
            self.value = value
        
        def get(self) -> T:
            return self.value
        
        def set(self, value: T) -> None:
            self.value = value
    
    class KeyValuePair(Generic[K, V]):
        """键值对类"""
        def __init__(self, key: K, value: V):
            self.key = key
            self.value = value
        
        def get_key(self) -> K:
            return self.key
        
        def get_value(self) -> V:
            return self.value
    
    def find_item(items: List[T], target: T) -> Optional[T]:
        """在列表中查找项目"""
        for item in items:
            if item == target:
                return item
        return None
    
    def merge_dicts(dict1: Dict[K, V], dict2: Dict[K, V]) -> Dict[K, V]:
        """合并字典"""
        result = dict1.copy()
        result.update(dict2)
        return result
    
    # 测试泛型类
    int_container = Container(42)
    str_container = Container("Hello")
    
    print(f"整数容器: {int_container.get()}")
    print(f"字符串容器: {str_container.get()}")
    
    # 测试键值对
    kv_pair = KeyValuePair("name", "张三")
    print(f"键: {kv_pair.get_key()}, 值: {kv_pair.get_value()}")
    
    # 测试泛型函数
    numbers = [1, 2, 3, 4, 5]
    found = find_item(numbers, 3)
    print(f"找到的数字: {found}")
    
    words = ["apple", "banana", "cherry"]
    found_word = find_item(words, "banana")
    print(f"找到的单词: {found_word}")
    
    # 测试字典合并
    dict1 = {"a": 1, "b": 2}
    dict2 = {"c": 3, "d": 4}
    merged = merge_dicts(dict1, dict2)
    print(f"合并后的字典: {merged}")

advanced_type_annotations()

2. 类型别名和NewType

from typing import NewType, List, Dict, Tuple

def type_aliases_and_newtypes():
    """类型别名和NewType示例"""
    
    # 类型别名
    UserID = int
    UserName = str
    UserEmail = str
    UserAge = int
    
    # 用户信息类型别名
    UserInfo = Dict[str, Union[str, int]]
    
    # 坐标类型别名
    Coordinate = Tuple[float, float]
    
    # 用户列表类型别名
    UserList = List[UserInfo]
    
    # NewType创建新类型
    UserID = NewType('UserID', int)
    ProductID = NewType('ProductID', int)
    
    def create_user(user_id: UserID, name: UserName, email: UserEmail) -> UserInfo:
        """创建用户"""
        return {
            "id": user_id,
            "name": name,
            "email": email
        }
    
    def get_user_by_id(users: UserList, user_id: UserID) -> Optional[UserInfo]:
        """根据ID获取用户"""
        for user in users:
            if user.get("id") == user_id:
                return user
        return None
    
    def calculate_distance(coord1: Coordinate, coord2: Coordinate) -> float:
        """计算两点间距离"""
        import math
        x1, y1 = coord1
        x2, y2 = coord2
        return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
    
    # 测试类型别名
    user_id = UserID(123)
    user_name = UserName("张三")
    user_email = UserEmail("zhangsan@example.com")
    
    user = create_user(user_id, user_name, user_email)
    print(f"用户信息: {user}")
    
    # 测试用户列表
    users = [
        {"id": 1, "name": "张三", "email": "zhangsan@example.com"},
        {"id": 2, "name": "李四", "email": "lisi@example.com"}
    ]
    
    found_user = get_user_by_id(users, UserID(1))
    print(f"找到的用户: {found_user}")
    
    # 测试坐标计算
    point1 = (0.0, 0.0)
    point2 = (3.0, 4.0)
    distance = calculate_distance(point1, point2)
    print(f"两点间距离: {distance}")

type_aliases_and_newtypes()

实际应用案例

1. 数据处理类

from typing import List, Dict, Optional, Union, Any, Callable
from dataclasses import dataclass

@dataclass
class DataPoint:
    """数据点类"""
    timestamp: float
    value: float
    label: Optional[str] = None

class DataProcessor:
    """数据处理器类"""
    
    def __init__(self, data: List[DataPoint]):
        self.data = data
    
    def filter_by_range(self, min_value: float, max_value: float) -> List[DataPoint]:
        """按值范围过滤数据"""
        return [point for point in self.data 
                if min_value <= point.value <= max_value]
    
    def group_by_label(self) -> Dict[str, List[DataPoint]]:
        """按标签分组数据"""
        groups: Dict[str, List[DataPoint]] = {}
        for point in self.data:
            label = point.label or "unknown"
            if label not in groups:
                groups[label] = []
            groups[label].append(point)
        return groups
    
    def apply_transformation(self, func: Callable[[float], float]) -> List[DataPoint]:
        """应用变换函数"""
        return [DataPoint(point.timestamp, func(point.value), point.label) 
                for point in self.data]
    
    def calculate_statistics(self) -> Dict[str, float]:
        """计算统计信息"""
        if not self.data:
            return {}
        
        values = [point.value for point in self.data]
        return {
            "mean": sum(values) / len(values),
            "min": min(values),
            "max": max(values),
            "count": len(values)
        }

def data_processing_example():
    """数据处理示例"""
    # 创建测试数据
    data = [
        DataPoint(1.0, 10.5, "A"),
        DataPoint(2.0, 15.2, "B"),
        DataPoint(3.0, 8.7, "A"),
        DataPoint(4.0, 20.1, "C"),
        DataPoint(5.0, 12.3, "B")
    ]
    
    # 创建数据处理器
    processor = DataProcessor(data)
    
    # 过滤数据
    filtered_data = processor.filter_by_range(10.0, 20.0)
    print(f"过滤后的数据点数量: {len(filtered_data)}")
    
    # 分组数据
    grouped_data = processor.group_by_label()
    for label, points in grouped_data.items():
        print(f"标签 {label}: {len(points)} 个数据点")
    
    # 应用变换
    def square(x: float) -> float:
        return x * x
    
    transformed_data = processor.apply_transformation(square)
    print(f"变换后的第一个值: {transformed_data[0].value}")
    
    # 计算统计信息
    stats = processor.calculate_statistics()
    print(f"统计信息: {stats}")

data_processing_example()

2. API接口类型注解

from typing import Dict, List, Optional, Union, Any
from dataclasses import dataclass
from enum import Enum

class Status(Enum):
    """状态枚举"""
    SUCCESS = "success"
    ERROR = "error"
    PENDING = "pending"

@dataclass
class APIResponse:
    """API响应类"""
    status: Status
    data: Optional[Any] = None
    message: Optional[str] = None
    code: int = 200

@dataclass
class User:
    """用户类"""
    id: int
    name: str
    email: str
    age: Optional[int] = None

class UserService:
    """用户服务类"""
    
    def __init__(self):
        self.users: Dict[int, User] = {}
        self.next_id = 1
    
    def create_user(self, name: str, email: str, age: Optional[int] = None) -> APIResponse:
        """创建用户"""
        try:
            # 验证邮箱格式
            if "@" not in email:
                return APIResponse(
                    status=Status.ERROR,
                    message="无效的邮箱格式",
                    code=400
                )
            
            # 创建用户
            user = User(
                id=self.next_id,
                name=name,
                email=email,
                age=age
            )
            
            self.users[self.next_id] = user
            self.next_id += 1
            
            return APIResponse(
                status=Status.SUCCESS,
                data=user,
                message="用户创建成功"
            )
            
        except Exception as e:
            return APIResponse(
                status=Status.ERROR,
                message=f"创建用户失败: {str(e)}",
                code=500
            )
    
    def get_user(self, user_id: int) -> APIResponse:
        """获取用户"""
        if user_id not in self.users:
            return APIResponse(
                status=Status.ERROR,
                message="用户不存在",
                code=404
            )
        
        return APIResponse(
            status=Status.SUCCESS,
            data=self.users[user_id]
        )
    
    def get_all_users(self) -> APIResponse:
        """获取所有用户"""
        return APIResponse(
            status=Status.SUCCESS,
            data=list(self.users.values())
        )
    
    def update_user(self, user_id: int, **kwargs: Any) -> APIResponse:
        """更新用户"""
        if user_id not in self.users:
            return APIResponse(
                status=Status.ERROR,
                message="用户不存在",
                code=404
            )
        
        try:
            user = self.users[user_id]
            for key, value in kwargs.items():
                if hasattr(user, key):
                    setattr(user, key, value)
            
            return APIResponse(
                status=Status.SUCCESS,
                data=user,
                message="用户更新成功"
            )
            
        except Exception as e:
            return APIResponse(
                status=Status.ERROR,
                message=f"更新用户失败: {str(e)}",
                code=500
            )

def api_service_example():
    """API服务示例"""
    service = UserService()
    
    # 创建用户
    response1 = service.create_user("张三", "zhangsan@example.com", 25)
    print(f"创建用户响应: {response1.status.value}, {response1.message}")
    
    response2 = service.create_user("李四", "lisi@example.com", 30)
    print(f"创建用户响应: {response2.status.value}, {response2.message}")
    
    # 获取用户
    user_response = service.get_user(1)
    if user_response.status == Status.SUCCESS:
        user = user_response.data
        print(f"获取用户: {user.name}, {user.email}")
    
    # 获取所有用户
    all_users_response = service.get_all_users()
    if all_users_response.status == Status.SUCCESS:
        users = all_users_response.data
        print(f"所有用户数量: {len(users)}")
    
    # 更新用户
    update_response = service.update_user(1, age=26, name="张三三")
    print(f"更新用户响应: {update_response.status.value}, {update_response.message}")

api_service_example()

mypy静态类型检查

1. 安装和配置mypy

# 安装mypy
pip install mypy

# 检查单个文件
mypy script.py

# 检查整个项目
mypy your_project/

# 生成配置文件
mypy --create-config

2. mypy配置示例

# mypy.ini 配置文件
[mypy]
python_version = 3.8
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_decorators = True
no_implicit_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
warn_no_return = True
warn_unreachable = True
strict_optional = True

[mypy-tests.*]
ignore_errors = True

3. 类型检查示例

from typing import List, Dict, Optional, Union

def type_checking_examples():
    """类型检查示例"""
    
    def process_numbers(numbers: List[int]) -> float:
        """处理数字列表"""
        if not numbers:
            return 0.0
        return sum(numbers) / len(numbers)
    
    def find_user(users: Dict[int, str], user_id: int) -> Optional[str]:
        """查找用户"""
        return users.get(user_id)
    
    def format_data(data: Union[str, int, float]) -> str:
        """格式化数据"""
        if isinstance(data, str):
            return data.upper()
        elif isinstance(data, (int, float)):
            return str(data)
        else:
            return "unknown"
    
    # 测试函数
    numbers = [1, 2, 3, 4, 5]
    average = process_numbers(numbers)
    print(f"平均值: {average}")
    
    users = {1: "张三", 2: "李四", 3: "王五"}
    user_name = find_user(users, 2)
    print(f"用户姓名: {user_name}")
    
    formatted = format_data("hello")
    print(f"格式化结果: {formatted}")

type_checking_examples()

总结

掌握Python类型注解是提高代码质量的关键:

  1. 基础类型注解:掌握基本类型、集合类型、可选类型的注解
  2. 函数注解:为函数参数和返回值添加类型信息
  3. 高级类型:理解泛型、类型变量、类型别名等高级概念
  4. 实际应用:在实际项目中使用类型注解
  5. 静态检查:使用mypy进行静态类型检查
  6. 最佳实践:遵循类型注解的最佳实践

通过系统学习这些概念,你将能够编写出更加清晰、可维护的Python代码,提高开发效率和代码质量。


转载请注明:周志洋的博客 » Python实用技巧-类型注解基础

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦