You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为继承类的构造函数动态设置类型提示?

问题描述

使用@dataclasses.dataclass装饰的类,其构造函数在Pyright等LSP工具中能自动提示类中定义的字段参数:

import dataclasses
@dataclasses.dataclass
class A:
    a: int
    b: float
A(<cursor location>)  # 此处能自动提示a和b参数

现在需要为自定义父类实现相同效果,让子类继承后,构造函数也能自动提示类中定义的字段参数。现有示例代码如下:

class Parent:
    def __init__(self, **kvargs):
       # 已实现参数校验等逻辑
       for k, v in kvargs.items():
            setattr(self, k, v)

    def __init_subclass(cls) -> None:
       # 如何基于子类的类型注解,为A.__init__添加a和b的类型提示?
       print(get_type_hints(cls))
       setattr(cls, "__init__", 
            # 如何从get_type_hints生成带类型注解的__init__函数签名?
            def __init__(a: int, b: int):
                super().__init__(a=a, b=b)
       )

class A(Parent):
    a: int
    b: float

A(<cursor location>)  # 希望此处能自动提示a和b参数

核心需求:已实现接收**kvargs的自定义__init__并完成参数校验,现在要为其添加类型注解信息,让LSP工具能识别并提供自动补全。

实现方案

要让LSP工具识别动态生成的构造函数参数,关键是给子类的__init__函数设置正确的__annotations__属性,同时生成符合类型提示要求的函数签名。可以通过以下步骤实现:

  • 提取子类类型注解:在__init_subclass__中用get_type_hints(cls)获取子类定义的所有字段及其类型。
  • 动态生成带注解的__init__:基于提取的类型注解,构造接收对应参数的__init__函数,内部调用父类__init__传递参数。
  • 配置注解与签名:确保生成的__init__的__annotations__(及可选的__signature__)符合LSP识别要求。

示例代码如下:

import inspect
from typing import get_type_hints

class Parent:
    def __init__(self, **kvargs):
        # 已实现的参数校验逻辑
        for k, v in kvargs.items():
            setattr(self, k, v)

    def __init_subclass(cls) -> None:
        # 获取子类的类型注解
        hints = get_type_hints(cls)
        if not hints:
            return

        # 动态生成__init__函数的源代码
        param_defs = ", ".join([f"{name}: {hint.__name__}" for name, hint in hints.items()])
        init_code = f"""
def __init__(self, {param_defs}):
    super().__init__({', '.join([f'{name}={name}' for name in hints.keys()])})
"""
        # 执行代码生成函数
        local_vars = {}
        exec(init_code, globals(), local_vars)
        new_init = local_vars["__init__"]

        # 设置函数注解
        new_init.__annotations__ = hints.copy()
        new_init.__annotations__["self"] = cls

        # 为LSP工具设置函数签名(部分工具依赖此属性)
        sig = inspect.signature(new_init)
        new_init.__signature__ = sig

        # 替换子类的__init__
        setattr(cls, "__init__", new_init)

class A(Parent):
    a: int
    b: float

# 此时在Pyright中输入A( 会自动提示a和b参数
A(a=1, b=2.5)

关键说明

  • 动态函数生成:通过exec生成匹配子类字段的__init__函数,确保参数名和类型注解与子类定义完全一致。
  • 注解配置:将子类的类型注解直接赋值给新__init__的__annotations__,LSP工具会读取该属性提供自动补全。
  • 签名优化:设置__signature__属性能让部分LSP工具更精准识别参数,提升补全体验。

如果不想用exec,也可以通过functools.wraps和inspect.Signature手动构造函数签名,但exec的方式更直观,能直接生成符合语法规范的函数定义。

内容的提问来源于stack exchange,提问作者KamilCuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 21:33:01