类型注解进阶:Union、Optional、Any 与 Callable

文章目录

Python 的类型注解不是为了把 Python 变成 Java------而是为了在保持动态灵活性的同时,获得静态检查的安全网。


从「注解只是提示」到「类型系统是工程工具」

Python 数据模型:双下划线方法的全景图 中,曾提到 Python 的类型系统是"可选的、渐进的"------不注解也能跑,注解了也不影响运行时行为。但"不影响运行时"并不意味着"没有价值"。

类型注解的核心价值在于静态分析阶段捕获错误:

python 复制代码
def greet(name: str) -> str:
    return f"Hello, {name}"

greet(42)  # mypy 报错: Argument 1 to "greet" has incompatible type "int"; expected "str"

运行时 greet(42) 不会报错(f"Hello, {42}" 完全合法),但语义上这是一个 bug------函数期望的是人名,传入的是数字。mypy 在代码执行之前就能发现这个问题。

这就是类型注解的工程定位:不改变运行时,但改变开发时的信心

本文将深入四个最核心的类型构造器------UnionOptionalAnyCallable------从语法到语义,从陷阱到最佳实践。


Union:一个值,多种可能类型

基本语法

Union[X, Y] 表示"这个值可以是 X 类型,也可以是 Y 类型":

python 复制代码
from typing import Union

def process_id(user_id: Union[int, str]) -> str:
    """处理用户 ID,支持整数和字符串两种格式"""
    if isinstance(user_id, int):
        return f"UID-{user_id:06d}"
    return f"UID-{user_id.strip()}"

print(process_id(42))        # UID-000042
print(process_id("admin"))   # UID-admin

多类型联合

Union 可以包含任意数量的类型:

python 复制代码
from typing import Union

JsonValue = Union[None, bool, int, float, str, list, dict]

def validate_json(value: JsonValue) -> bool:
    """验证 JSON 值的合法性"""
    if isinstance(value, (list, dict)):
        return all(validate_json(v) for v in value) if isinstance(value, list) else True
    return isinstance(value, (type(None), bool, int, float, str))

Python 3.10+ 的新语法:X | Y

PEP 604 引入了更简洁的联合类型语法,不再需要导入 Union

python 复制代码
# Python 3.10+ 写法
def process_id(user_id: int | str) -> str:
    if isinstance(user_id, int):
        return f"UID-{user_id:06d}"
    return f"UID-{user_id.strip()}"

# 多类型联合
JsonValue = None | bool | int | float | str | list | dict

| 语法与 Union 完全等价,且可以在 isinstance() 中使用:

python 复制代码
# Python 3.10+ 的 isinstance 支持 | 语法
def handle(value: int | str | float) -> str:
    if isinstance(value, int | float):
        return f"numeric: {value}"
    return f"string: {value}"

Union 的语义陷阱:不是"任意一个"

Union[int, str] 的语义是"值恰好是 int 或 str 其中之一",而不是"值可以随意在 int 和 str 之间转换":

python 复制代码
from typing import Union

def add(left: Union[int, str], right: Union[int, str]) -> Union[int, str]:
    """危险:返回类型取决于输入组合,调用方无法安全使用"""
    if isinstance(left, int) and isinstance(right, int):
        return left + right      # int + int → int
    return f"{left}{right}"       # 任意组合 → str

result = add(1, 2)       # 返回 int
result = add("a", "b")   # 返回 str
result = add(1, "b")     # 返回 str------调用方拿到 Union[int, str],必须再判断

这种"输出类型取决于输入类型组合"的场景,正确的做法是使用函数重载@overload):

python 复制代码
from typing import overload, Union

@overload
def add(left: int, right: int) -> int: ...
@overload
def add(left: str, right: str) -> str: ...
@overload
def add(left: int, right: str) -> str: ...
@overload
def add(left: str, right: int) -> str: ...

def add(left: Union[int, str], right: Union[int, str]) -> Union[int, str]:
    if isinstance(left, int) and isinstance(right, int):
        return left + right
    return f"{left}{right}"

reveal_type(add(1, 2))      # mypy 推断为 int
reveal_type(add("a", "b"))  # mypy 推断为 str
reveal_type(add(1, "b"))    # mypy 推断为 str

原则 :当函数的返回类型依赖于多个参数的组合时,用 @overload 提供精确的类型签名,而不是让所有路径都返回 Union

Union 缩化(Narrowing)

mypy 支持通过 isinstancetype() 检查自动缩化 Union 类型:

python 复制代码
from typing import Union

def process(value: Union[int, str]) -> int:
    # 此处 value 的类型是 Union[int, str]
    if isinstance(value, int):
        # mypy 知道此处 value 一定是 int
        return value ** 2
    # mypy 知道此处 value 一定是 str(Union 的剩余分支)
    return len(value)

类型缩化是类型安全的基石------永远不要用 type: ignore 跳过缩化,而是用 isinstance 让 mypy 自动推断


Optional:可能为空的值

本质:Optional[X] 就是 Union[X, None]

python 复制代码
from typing import Optional

# 这两种写法完全等价
def find_user(name: str) -> Optional[str]:
    ...

def find_user(name: str) -> Union[str, None]:
    ...

Python 3.10+ 的等价写法:

python 复制代码
def find_user(name: str) -> str | None:
    ...

为什么需要 Optional 而不是直接写 None

语义清晰度。Optional[str] 传达的是"这个位置可能没有值",而 Union[str, None] 传达的是"这个值可以是字符串或 None"。虽然类型等价,但语义重心不同

  • Optional 强调"可选性"------参数或返回值可以缺失
  • Union 强调"多态性"------值可以是多种类型之一
python 复制代码
from typing import Optional

# 场景一:可选参数------强调"可选性",用 Optional
def connect(host: str, port: Optional[int] = None) -> None:
    """port 不传则使用默认端口"""
    actual_port = port if port is not None else 3306
    print(f"Connecting to {host}:{actual_port}")

# 场景二:查找函数------返回值可能不存在,用 Optional
def find_config(key: str) -> Optional[str]:
    """查找配置项,可能不存在"""
    config = {"host": "localhost", "port": "3306"}
    return config.get(key)  # dict.get 默认返回 None

result = find_config("timeout")
if result is not None:
    print(result.upper())  # mypy 确认此处 result 是 str

Optional 的常见误用

误用一:用 Optional 包裹容器类型

python 复制代码
# ❌ 错误:Optional[list] 暗示"可能是 list,也可能是 None"
# 但实际上"空列表"和 None 的语义完全不同
def get_items(user_id: int) -> Optional[list]:
    ...

# ✅ 正确:返回空列表表示"没有元素",用 None 表示"查询失败"
def get_items(user_id: int) -> list:
    """返回用户的条目列表,没有条目时返回空列表"""
    return []

def find_items(user_id: int) -> Optional[list]:
    """查找用户的条目列表,用户不存在时返回 None"""
    ...

误用二:不处理 None 就直接使用

python 复制代码
from typing import Optional

def get_name() -> Optional[str]:
    return None

# ❌ mypy 报错: Item "None" of "Optional[str]" has no attribute "upper"
name = get_name()
print(name.upper())  # 运行时 AttributeError: 'NoneType' object has no attribute 'upper'

# ✅ 先判断 None
name = get_name()
if name is not None:
    print(name.upper())

Python 3.10+ 的 | None vs Optional

python 复制代码
# 传统写法
from typing import Optional
def find(id: int) -> Optional[str]: ...

# 3.10+ 写法(推荐,更简洁)
def find(id: int) -> str | None: ...

两者完全等价,| None 是 PEP 604 的一部分,与 Union| 语法统一。对于新项目,推荐使用 | None


Any:类型系统的逃生舱

Any 的语义:放弃类型检查

Any 是类型系统中最"危险"的类型------它告诉 mypy:"不要检查这个值,任何操作都可以"。

python 复制代码
from typing import Any

def process(data: Any) -> Any:
    # mypy 不会报任何错------因为 Any 兼容一切类型
    result = data.upper()       # OK(即使 data 可能是 int)
    result = data + 1           # OK(即使 data 可能是 str)
    result = data.nonexistent() # OK(即使方法不存在)
    return result

process(42)       # 运行时 AttributeError: 'int' object has no attribute 'upper'
process("hello")  # 运行时 TypeError: can only concatenate str (not "int") to str

Any 的传播性

Any 具有传染性 ------一旦一个值被推断为 Any,所有与之交互的值也会变成 Any

python 复制代码
from typing import Any

def untyped_function(x: Any) -> None:
    y = x + 1       # y 的类型被推断为 Any
    z = y.upper()   # z 的类型被推断为 Any
    print(z)         # 整个调用链都是 Any

# 从 Any 传播到外部
result: Any = some_untyped_library()
name: str = result.name  # name 的类型标注是 str,但实际值可能是 Any
length = len(name)       # length 被推断为 int,但如果 name 不是 str......

这就像 Rust 的 unsafe 块------一旦进入 Any 的世界,类型安全不再有保证,必须手动恢复。

Any 的合理使用场景

尽管危险,Any 在以下场景是必要的:

场景一:与无类型注解的第三方库交互

python 复制代码
from typing import Any
import legacy_module  # 没有 type stub 的老库

def safe_wrapper(data: dict[str, Any]) -> str:
    """包装无类型库调用,在边界处收缩类型"""
    # legacy_module.process 返回 Any
    raw_result: Any = legacy_module.process(data)
    
    # 在边界处做运行时校验,恢复类型安全
    if isinstance(raw_result, str):
        return raw_result
    raise TypeError(f"Expected str, got {type(raw_result)}")

场景二:类型注解还不支持的表达

python 复制代码
from typing import Any

# 自序列化场景:JSON 值的类型在运行时才能确定
def parse_value(raw: str) -> Any:
    """解析 JSON 值,类型在运行时决定"""
    import json
    return json.loads(raw)  # 可能是 int、str、list、dict......

# 正确做法:用 Union 替代 Any 表示"已知的多种类型"
JsonValue = None | bool | int | float | str | list["JsonValue"] | dict[str, "JsonValue"]

def parse_value_typed(raw: str) -> JsonValue:
    import json
    return json.loads(raw)

场景三:objectAny 的区别

python 复制代码
def use_object(x: object) -> None:
    # object 是所有类型的父类,但只能调用 object 的方法
    print(hash(x))     # OK------hash 是 object 的方法
    print(x.upper())   # ❌ mypy 报错------object 没有 upper 方法

def use_any(x: Any) -> None:
    # Any 允许任意操作,不做任何检查
    print(x.upper())   # OK(mypy 不报错)
    print(x + 1)       # OK(mypy 不报错)

原则 :如果只需要表示"任意类型",优先用 object;只有在需要绕过类型检查时才用 Any

cast:运行时不生效的类型断言

当开发者比 mypy 更了解类型时,使用 cast 而不是 Any

python 复制代码
from typing import cast

def get_config() -> dict[str, int]:
    """从外部源获取配置,开发者知道值都是 int"""
    raw: dict[str, object] = load_raw_config()  # 返回类型不够精确
    
    # cast 只在类型检查时生效,运行时是空操作
    return cast(dict[str, int], raw)

# ❌ 危险的替代方案:用 Any 绕过
def get_config_unsafe() -> dict[str, int]:
    raw: dict[str, Any] = load_raw_config()
    return raw  # mypy 不报错,但类型安全完全丧失

castAny 的关键区别:

  • cast局部的、一次性的类型断言,影响范围有限
  • Any全局的、传染性的,会沿调用链传播

Callable:函数也是类型

基本语法

Callable[[Arg1Type, Arg2Type], ReturnType] 表示一个可调用对象的类型签名:

python 复制代码
from typing import Callable

def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    """将一个二元函数应用到两个整数上"""
    return func(a, b)

def add(x: int, y: int) -> int:
    return x + y

def multiply(x: int, y: int) -> int:
    return x * y

print(apply(add, 3, 4))       # 7
print(apply(multiply, 3, 4))  # 12

回调函数场景

Callable 最常见的用途是类型化回调函数和策略模式:

python 复制代码
from typing import Callable

# 策略模式:不同的折扣策略
DiscountStrategy = Callable[[float], float]

def no_discount(price: float) -> float:
    return price

def percentage_discount(price: float, rate: float = 0.1) -> float:
    return price * (1 - rate)

def fixed_discount(price: float, amount: float = 100) -> float:
    return max(0, price - amount)

class Order:
    def __init__(self, total: float, discount_fn: DiscountStrategy) -> None:
        self.total = total
        self.discount_fn = discount_fn
    
    def final_price(self) -> float:
        return self.discount_fn(self.total)

# 使用
order1 = Order(1000, no_discount)
order2 = Order(1000, lambda p: percentage_discount(p, 0.2))

print(order1.final_price())  # 1000.0
print(order2.final_price())  # 800.0

无参数可调用:Callable[[], T]

python 复制代码
from typing import Callable

def lazy_init(factory: Callable[[], str]) -> str:
    """延迟初始化:只在需要时调用工厂函数"""
    return factory()

# 无参数的工厂函数
result = lazy_init(lambda: "initialized")
print(result)  # initialized

可调用但不关心签名:Callable[..., T]

...(Ellipsis)表示"接受任意参数":

python 复制代码
from typing import Callable

def log_and_call(func: Callable[..., int], *args: object, **kwargs: object) -> int:
    """调用任意函数并记录日志,不关心参数签名"""
    print(f"Calling {func.__name__}")
    return func(*args, **kwargs)  # type: ignore[arg-type]

def compute(a: int, b: int) -> int:
    return a + b

print(log_and_call(compute, 1, 2))  # Calling compute → 3

注意Callable[..., T] 放弃了参数类型的检查,应尽量少用。如果参数可变,优先使用 Protocol 定义可调用协议(后续文章会展开)。

Python 3.10+:Callable 的局限与替代

Callable 有一个根本限制------只能描述位置参数,无法表达关键字参数、默认值、*args**kwargs

python 复制代码
from typing import Callable

# ❌ Callable 无法表达"第二个参数有默认值"
def register(callback: Callable[[str, int], None]) -> None:
    ...

# 实际上 callback 可能是 def on_event(name: str, priority: int = 0): ...
# 但 Callable 无法编码这个信息

Python 3.12+ 的 PEP 695 提供了更强大的语法(后文会涉及),但在此之前,需要用 Protocol 来描述复杂的可调用签名:

python 复制代码
from typing import Protocol

class EventHandler(Protocol):
    def __call__(self, name: str, priority: int = 0) -> None: ...

def register(handler: EventHandler) -> None:
    handler("click")  # OK,priority 使用默认值
    handler("click", priority=1)  # OK

四者的关系:类型系统的层次

具体类型

str, int, list[int]
Union[X, Y]

多选一
Optional[X]

Union[X, None] 的语法糖
Callable[[Args], R]

函数签名类型
object

所有类型的父类

只能调用 object 方法
Any

类型系统的逃生舱

兼容一切操作

从具体到抽象的谱系

类型构造器 语义 类型检查行为 适用场景
具体类型 int 值恰好是这个类型 完全检查 参数类型明确
Union[X, Y] 值是 X 或 Y 检查 + 缩化 参数接受多种类型
Optional[X] 值是 X 或 None 检查 + None 判断 可选参数、可能缺失的返回值
Callable[[A], R] 函数签名 检查参数和返回类型 回调、策略模式
object 任意值(只读) 只允许 object 方法 不关心具体类型、仅需哈希/打印
Any 任意值(读写) 完全不检查 与无类型库交互、类型系统边界

工程实战:API 响应处理的类型安全方案

将四个类型构造器组合在一个真实的 API 响应处理场景中:

python 复制代码
"""API 响应处理器:演示 Union/Optional/Any/Callable 的工程化组合"""
from typing import Any, Callable, Optional, Union
import json


# ========== 第一层:原始数据 → 类型化数据 ==========

# JSON 响应可能是多种结构
ApiResponse = Union[
    dict[str, Any],    # 成功响应
    str,               # 错误消息
    None,              # 超时或无响应
]

def parse_response(raw: str) -> ApiResponse:
    """解析原始 API 响应,区分三种情况"""
    if not raw:
        return None  # 空响应 → None
    
    try:
        data = json.loads(raw)
        if isinstance(data, dict):
            return data
        return str(data)  # 非 dict 响应视为错误消息
    except json.JSONDecodeError:
        return raw  # JSON 解析失败,返回原始字符串


# ========== 第二层:类型缩化 + 安全访问 ==========

def extract_field(
    response: ApiResponse,
    field: str,
) -> Optional[str]:
    """从 API 响应中安全提取字段"""
    # 类型缩化:排除 None 和 str 分支
    if response is None:
        return None
    
    if isinstance(response, str):
        return None  # 错误消息中没有字段
    
    # 此处 response 一定是 dict[str, Any]
    value: Any = response.get(field)
    
    if isinstance(value, str):
        return value
    return None


# ========== 第三层:可配置的响应处理 ==========

# 处理器类型:接受响应,返回布尔表示是否成功
ResponseHandler = Callable[[ApiResponse], bool]

def success_handler(response: ApiResponse) -> bool:
    """成功处理器:检查响应是否包含数据"""
    if isinstance(response, dict):
        return "data" in response
    return False

def retry_handler(response: ApiResponse) -> bool:
    """重试处理器:检查是否需要重试"""
    if response is None:
        return True  # 超时需要重试
    if isinstance(response, str) and "timeout" in response.lower():
        return True
    return False


def process_with_handler(
    raw: str,
    handler: ResponseHandler,
) -> bool:
    """用可配置的处理器处理 API 响应"""
    response = parse_response(raw)
    return handler(response)


# ========== 使用示例 ==========

# 场景一:正常响应
result = extract_field('{"name": "Alice", "age": 30}', "name")
print(f"Name: {result}")  # Name: Alice

# 场景二:空响应
result = extract_field('', "name")
print(f"Name: {result}")  # Name: None

# 场景三:成功检测
is_success = process_with_handler('{"data": [1, 2, 3]}', success_handler)
print(f"Success: {is_success}")  # Success: True

# 场景四:重试检测
should_retry = process_with_handler('Connection timeout', retry_handler)
print(f"Should retry: {should_retry}")  # Should retry: True

类型注解的运行时影响:get_type_hints__annotations__

类型注解存储在 __annotations__ 字典中,可以通过 typing.get_type_hints() 获取解析后的类型:

python 复制代码
from typing import Optional, get_type_hints, Union

def example(name: str, age: Optional[int] = None) -> Union[str, int]:
    ...

# 直接访问原始注解(字符串形式)
print(example.__annotations__)
# {'name': <class 'str'>, 'age': typing.Optional[int], 'return': typing.Union[str, int]}

# get_type_hints 解析前向引用和字符串注解
print(get_type_hints(example))
# {'name': <class 'str'>, 'age': typing.Union[int, NoneType], 'return': typing.Union[str, int]}

from __future__ import annotations 的影响

PEP 563(Python 3.7+,默认延迟求值注解)改变了注解的存储方式:

python 复制代码
from __future__ import annotations
from typing import Optional

def example(name: str, age: Optional[int] = None) -> str | None:
    ...

# 所有注解都变成字符串,不再在定义时求值
print(example.__annotations__)
# {'name': 'str', 'age': 'Optional[int]', 'return': 'str | None'}

# get_type_hints 会尝试求值字符串
print(get_type_hints(example))
# 需要注解中的名称在模块命名空间中可用

注意from __future__ import annotations 让所有注解变成字符串,这可以避免前向引用问题,但也意味着注解在定义时不会被求值------如果注解中有拼写错误,只有调用 get_type_hints() 时才会发现。


mypy 配置与常用检查项

mypy 的严格级别

ini 复制代码
# pyproject.toml 中的 mypy 配置
[tool.mypy]
python_version = "3.12"
strict = true
# 等价于启用以下所有选项:
# disallow_untyped_defs = true         # 函数必须有类型注解
# disallow_any_generics = true          # 泛型必须指定类型参数
# warn_return_any = true                # 返回 Any 时警告
# disallow_untyped_calls = true         # 调用无类型函数时报错
# warn_unused_ignores = true            # 未使用的 type: ignore 报错
# no_implicit_optional = true           # 禁止隐式 Optional

常见 mypy 报错与修复

python 复制代码
# 报错一:Incompatible return type
def maybe_get() -> str:
    if some_condition:
        return "found"
    return None  # ❌ mypy: Incompatible return value type (got "None", expected "str")

# 修复:使用 Optional
def maybe_get() -> str | None:
    if some_condition:
        return "found"
    return None

# 报错二:Item "None" has no attribute
def process(data: str | None) -> int:
    return data.upper()  # ❌ mypy: Item "None" of "Optional[str]" has no attribute "upper"

# 修复:先判断 None
def process(data: str | None) -> int:
    if data is None:
        return 0
    return len(data.upper())

# 报错三:Implicit Optional(no_implicit_optional = true 时)
def greet(name: str = None) -> str:  # ❌ mypy: Implicit Optional
    return f"Hello, {name}"

# 修复:显式声明 Optional
def greet(name: str | None = None) -> str:
    if name is None:
        return "Hello, anonymous"
    return f"Hello, {name}"

类型注解的性能考量

类型注解在运行时 的开销极小------CPython 在定义函数/类时把注解存入 __annotations__ 字典,之后不再访问。但在以下场景中需要注意:

get_type_hints() 的性能

python 复制代码
from typing import get_type_hints
import time

class BigModel:
    """含 50 个字段的模型"""
    field_00: str
    field_01: int
    # ... 省略 field_02 ~ field_48
    field_49: bool

# get_type_hints 在每次调用时都重新求值
start = time.perf_counter()
for _ in range(10000):
    get_type_hints(BigModel)
elapsed = time.perf_counter() - start
print(f"10000 calls to get_type_hints: {elapsed:.4f}s")
# 约 0.5~1.0s,因为每次都重新解析注解

Pydantic 与 model_rebuild

Pydantic v2 使用 Rust 核心,运行时类型验证非常快。但如果模型包含前向引用,需要在所有类定义完成后调用 model_rebuild()

python 复制代码
from pydantic import BaseModel

class Node(BaseModel):
    value: int
    children: list["Node"]  # 前向引用

Node.model_rebuild()  # 解析前向引用,构建验证器

选型决策树:什么时候用什么







只读操作
需要任意操作



需要注解一个类型
值可能是

多种类型之一?
值可能为 None?
类型是否已知

且数量有限?
使用具体类型

str, int, list[int]
使用 Optional[X]

或 X | None
使用 Union[X, Y, Z]

或 X | Y | Z
是否完全不

关心类型?
使用 object

只允许 object 方法
使用 Any

并在边界收缩
值是可调用对象?
签名是否简单?
使用 Callable[[Args], R]
使用 Protocol

定义可调用协议


最佳实践总结

类型注解的渐进式策略

  1. 公共 API 优先:先为模块的公共函数、类方法添加注解,内部实现可以后补
  2. 返回值优先于参数:返回值类型决定了调用方的使用方式,优先注解
  3. 从窄到宽 :用 具体类型 → Optional → Union → object → Any 的顺序,尽量使用最窄的类型
  4. 在边界收缩 Any :与外部库交互时,入口参数可以是 Any,但内部函数应立即转换为具体类型

反模式清单

反模式 问题 修复
def foo(x) 无注解 mypy 无法检查 至少加 x: object
def foo(x: Any) 滥用 Any 完全丧失类型安全 用具体类型或 Union
Optional[list] 返回值 混淆空列表和 None 空列表用 list,查询失败用 Optional
Union 返回值不缩化 调用方必须判断 @overload 精确签名
cast(Any, x) 绕过检查 等于没有注解 用具体类型 cast(dict[str, int], x)
Callable[..., Any] 完全不检查 用 Protocol 定义签名

mypy 推荐配置

ini 复制代码
[tool.mypy]
python_version = "3.12"
strict = true
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true

# 第三方库缺少 stub 时
[[tool.mypy.overrides]]
module = "legacy_module.*"
ignore_missing_imports = true

小结

构造器 一句话定位 核心风险 核心原则
Union 多选一的类型联合 不缩化则调用方痛苦 搭配 isinstance 做类型缩化,复杂场景用 @overload
Optional 可能不存在的值 不判 None 就使用 永远在使用前检查 is not None,用 `
Any 类型系统的逃生舱 传染性扩散 只在边界使用,进入内部立刻收缩为具体类型
Callable 函数签名类型 无法表达默认值和 *args 简单签名用 Callable,复杂签名用 Protocol

类型注解不是"写对了就完事"------它是一个持续演进的工程实践。从具体类型出发,在需要时引入 UnionOptional,在边界处处理 Any,用 Callable 描述回调契约------这四者是类型注解工程化的基石。


如果这篇文章对理解 Python 类型注解有帮助,点赞收藏让更多人看到!关注专栏,持续获取 Python 进阶干货。

相关推荐
basketball6166 小时前
C++面试考点 头文件与实现文件形式
开发语言·c++
爱喝热水的呀哈喽6 小时前
gpt:RAG步骤
人工智能·python·机器学习
历程里程碑6 小时前
56 . 高效ET非阻塞IO服务器设计指南
java·运维·服务器·开发语言·数据结构·c++·排序算法
Fleshy数模6 小时前
课堂教学质量评估系统:基于加权欧氏距离的评分实现
python·llm
恣艺6 小时前
Python 游戏开发与文件处理:PyGame + Turtle + openpyxl + python-docx + PyPDF2
开发语言·python·pygame
高林雨露7 小时前
kotlin 相关code
开发语言·kotlin
_山海7 小时前
用langchain 通过text-embedding-3-small生成embedding
python·langchain·llm
我还记得那天7 小时前
函数的递归调用
c语言·开发语言·visualstudio
zhangfeng11337 小时前
ThinkPHP5 事件系统的标准最佳实践 事件系统的完整设计逻辑tags.php tags.php(事件地图)
android·开发语言·php