title: type annotation description: Type annotations (Type Hints) are a milestone feature introduced in Python 3.5+. It does not change the dynamic characteristics of Python (there is still no mandatory check at runtime), but it tells development tools and frameworks through "metadata": what should be here and what will be returned there.

1. Preface: Why does Python also need a "sense of type constraints"?

Python is a language that is designed to be “write whatever you want”. There is no need to declare types, and the script can run in just a few dozen lines. But once the project exceeds 1,000 lines, or requires multiple people to collaborate to develop the back-end API, this "freedom" quickly turns into a burden.

To give a small real example: a colleague optimized the return value of the login interfaceuser_metafield, put the insidelevelfromintQuietly changed it to one with a progress bardict. As a result, the script to export Excel crashed a bunch of times.TypeError: unsupported operand type(s) for +: 'dict' and 'int', I checked the Git records for half an hour before I found the missing line.

The bigger pain points are actually hidden in daily development:

  • Variable Quiz Conference: Take over someone else's function, and the parameters are calleddata, the return value is calledresult——What is it?listdict? or may at any time beNonebomb?
  • IDE degradation: PyCharm / VS Code cannot even pop up the most basic automatic completion, and can only rely on human memory to remember class names and method names.
  • Rely on testing to find out: Many hidden bugs (such as the wrong type of key in the dictionary) will only be exposed under specific input.

Type Hints are the "lightweight antidote" to solve these pain points. It does not change the dynamic characteristics of Python at all (the interpreter still does not perform mandatory checks at runtime), but only tells development tools and partners through "metadata": what should be passed here and what will be returned there.


2. Basics: "Label" variables and functions

The core syntax is very simple:

  • Add after the variable name:Add type;
  • Function return value is preceded by->Add type.

1. Single-valued variables and basic functions

# 单值变量(Python 3.6+ 支持直接标注)
user_name: str = "DaomanLab"
user_level: int = 5
is_member: bool = True

# 带输入输出注解的函数
def generate_greeting(name: str, level: int) -> str:
    """生成会员专属欢迎语"""
    if level >= 5:
        return f"🎉 钻石会员 {name} 下午好!"
    return f"👋 普通会员 {name} 下午好!"

# ⚠️ 下面这行代码静态检查会报错,但解释器仍然可以执行
# generate_greeting(123, "Gold")

💡 One sentence summary: Type annotations are like "sticky notes" attached to the code. Development tools (such as mypy, PyCharm) will read these tags to help us check errors, but the Python interpreter itself does not read them, so it does not affect the operation.


3. Advanced: Annotation of containers and multi-state values

deal withlistdictWhen this type of container, or the value "may be A or B", we need to usetypingTools provided by the module. Note: Starting from Python 3.9, many commonly used tools can already use lowercase directly. There is no need to start fromtypingImport.

1. Common container types

# ✅ Python 3.9+ 推荐的内置写法(完全不需要导入)
price_list: list[float] = [199.9, 299.9, 399.9]
course_dict: dict[str, bool | float] = {"Python": True, "price": 99.9}
site_coords: tuple[int, int, str] = (30, 120, "Hangzhou")

# 📝 兼容旧版本(Python < 3.9)的写法
from typing import List, Dict, Tuple
old_price_list: List[float] = [1.1, 2.2]
old_course_dict: Dict[str, bool | float] = {"Java": False}

2. Multiple states and nullable values

  • ** Union Type (Union)**: used when a value is "A or B";
  • optional type (Optional): When a value is "A orNone” is used (essentiallyUnion[A, None]abbreviation).
from typing import Union, Optional

# 📝 兼容性写法(3.5 - 3.10 通用)
def fetch_course(course_id: Union[int, str]) -> Optional[dict]:
    """支持数字 ID 或字符串 ID 查课程,查不到返回 None"""
    mock_db = {1: "Python进阶", "course-02": "FastAPI入门"}
    if course_id in mock_db:
        return {"title": mock_db[course_id], "status": "published"}
    return None

# ✅ Python 3.10+ 极简写法:用 | 替代 Union,直接写 None 替代 Optional
def fetch_course_v2(course_id: int | str) -> dict | None:
    mock_db = {1: "Python进阶", "course-02": "FastAPI入门"}
    return {"title": mock_db[course_id], "status": "published"} if course_id in mock_db else None

**🎯 Why is the new writing method recommended? ** int | strCompareUnion[int, str]Shorter, more intuitive, and no import required, reducing mental load.


4. Core Chapter: Classes, Callback Functions and Pydantic Models

1. Mark callback function (Callable)

Python often needs to pass functions as parameters (such as sortedkey, the callback of the asynchronous framework), you can use it at this timeCallableto mark.

from typing import Callable

# 标注规则:Callable[[参数1类型, 参数2类型, ...], 返回值类型]
def apply_math_op(a: int, b: int, op_func: Callable[[int, int], int]) -> int:
    return op_func(a, b)

# 传加法 lambda
print(apply_math_op(1, 2, lambda x, y: x + y))  # 输出 3
# 传乘法 lambda
print(apply_math_op(3, 4, lambda x, y: x * y))  # 输出 12

⚠️ Be careful with arrows:Callable[[int, int], int]The first square bracket is the type of the parameter list, and the second is the return value type. Don't get confused.

2. Pydantic model (the "golden partner" of back-end development)

If you are writing a FastAPI interface, you will definitely encounter Pydantic. It directly turns type annotations into automatic data verification rules, and can even generate API documents with one click!

from pydantic import BaseModel

# 1. 定义一个“课程创建请求”的模型
class CourseCreate(BaseModel):
    title: str          # 必填字段
    price: float        # 必填字段
    is_published: bool = False  # 带默认值的选填字段

# 2. 模拟 FastAPI 接口逻辑
def create_course_api(req_data: CourseCreate):
    # Pydantic 会自动帮你做这些事:
    # ✅ 检查前端 JSON 是否缺了必填字段
    # ✅ 检查字段类型(比如 price 传了 "abc" 会直接报错)
    # ✅ 把 JSON 自动转成 Python 对象,支持点语法访问
    print(f"✅ 课程创建成功:{req_data.title},定价:{req_data.price}")

# 3. 测试一下(正常输入)
valid_req = CourseCreate(title="Vue3入门", price=49.9)
create_course_api(valid_req)

CourseCreate(title="Vue3入门")A validation error will be triggered immediately because the requiredpriceFields - These types of bugs can be discovered during development rather than exploding later in production.


5. Engineering Practice: Don’t let type annotations become a burden

Although type annotations are good, overuse will slow down the pace of development. Share three practical suggestions:

1. Don’t annotate local variables that are “visible at a glance”

in loopi, simple conversion within the functionsorted_listSuch variables can be inferred automatically by the IDE and do not require manual annotation at all.

# ❌ 过度注解(完全没必要)
nums: list[int] = [3, 1, 2]
sorted_nums: list[int] = sorted(nums)
for i: int in range(len(sorted_nums)):
    print(sorted_nums[i])

# ✅ 简洁写法(IDE 照样自动补全)
nums = [3, 1, 2]
sorted_nums = sorted(nums)
for i in range(len(sorted_nums)):
    print(sorted_nums[i])

Principle: Only annotate function signatures, class attributes, and public APIs, and leave local variables to type inference.

2. UseAnyAs an "escape hatch"

When you really can't determine the type (such as parsing extremely complex nested JSON, connecting to black box third-party interfaces), you can useAnyTell the static checking tool explicitly: "Give me a break here."

from typing import Any

def parse_weird_json(raw: str) -> Any:
    """解析第三方的超复杂 JSON,暂时无法确定返回类型"""
    import json
    return json.loads(raw)

AnyThis is not an excuse to be lazy, but an honest signal to admit that "the current information is insufficient and will be refined later."

3. CooperationmypyDo a "static code physical examination"

Type annotations are written but not checked, which is equivalent to writing in vain. Install and run the terminalmypyScanning the project can reveal a large number of hidden type errors.

# 安装
pip install mypy

# 扫描单个文件
mypy your_script.py

# 扫描整个项目(推荐加上 --strict,开启严格模式)
mypy your_project/ --strict

WillmypyIntegrated into the CI process, the type safety of the code can be continuously ensured.


Conclusion

Type annotations are a key step for Python to move from a "fast scripting language" to an "industrial-level collaborative language". It will not make your writing speed slower (post-completion and refactoring are much faster), nor will it neuter the flexibility of Python. Master it, and your code can be upgraded from "just enough to run" to "clear, robust, and understandable at a glance."