如何让子类__init__继承父类参数并支持类型检查与IDE补全?
问题描述
在Python类继承场景中,定义了如下代码:
class A: def __init__(self, filepath: str, debug=False): self.filepath = filepath self.debug = debug class B(A): def __init__(self, portnumber: int, **kwargs): super(B, self).__init__(**kwargs) self.portnumber = portnumber
为满足类型检查与代码补全需求,希望将父类A.__init__的参数列表“转发”给子类B.__init__,使类型检查器能正确推断B.__init__的签名,IDE可提供有效的补全与检查。
已知将A、B声明为dataclass可实现类似效果:
from dataclasses import dataclass @dataclass class A: filepath: str debug: bool = False @dataclass class B(A): portnumber: int = 42
请问是否存在仅针对__init__方法的类似方案?比如使用类似@extends(A.__init__)的特殊装饰器?
解决方案
当然有针对__init__方法的专用方案,以下是几种实用选项:
1. 利用Python 3.10+类型语法转发参数
通过typing.ParamSpec和Unpack可以在类型层面精确转发父类__init__的参数,不修改运行时逻辑:
from typing import ParamSpec, Self P = ParamSpec("P") class A: def __init__(self, filepath: str, debug: bool = False): self.filepath = filepath self.debug = debug class B(A): def __init__(self, portnumber: int, *args: P.args, **kwargs: P.kwargs) -> Self: super().__init__(*args, **kwargs) self.portnumber = portnumber
这种写法能让mypy、pyright等类型检查器正确识别父类参数,IDE也能提供完整的补全提示。
2. 自定义装饰器实现参数转发
可以自己实现一个extends_init装饰器,通过解析父类__init__的签名,动态更新子类__init__的类型提示:
import inspect from functools import wraps from typing import Callable def extends_init(parent_init: Callable) -> Callable: def decorator(child_init: Callable) -> Callable: # 解析父类与子类的方法签名 parent_sig = inspect.signature(parent_init) child_sig = inspect.signature(child_init) # 合并参数:子类参数在前,父类非self参数在后 new_params = list(child_sig.parameters.values()) for name, param in parent_sig.parameters.items(): if name != "self": new_params.append(param) new_sig = child_sig.replace(parameters=new_params) # 更新子类__init__的签名 @wraps(child_init) def wrapper(*args, **kwargs): return child_init(*args, **kwargs) wrapper.__signature__ = new_sig return wrapper return decorator # 使用示例 class A: def __init__(self, filepath: str, debug: bool = False): self.filepath = filepath self.debug = debug class B(A): @extends_init(A.__init__) def __init__(self, portnumber: int, **kwargs): super().__init__(**kwargs) self.portnumber = portnumber
这个装饰器仅修改方法的签名信息,不改变原有运行逻辑,能让类型检查器和IDE识别到父类的参数。
3. 用第三方库简化实现
如果不想自己编写装饰器,可以使用attrs库——它比dataclass更灵活,支持自动继承父类的__init__参数,同时保留类的其他自定义特性:
from attrs import define @define class A: filepath: str debug: bool = False @define class B(A): portnumber: int = 42
attrs会自动处理父类的参数转发,不需要额外配置,且支持更多自定义选项。
内容的提问来源于stack exchange,提问作者LeGEC
相关产品推荐
相关产品推荐

