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

Pyright/PyCharm重载解析优先级问题:如何让类型匹配优先于参数数量匹配@overload

Pyright/PyCharm重载解析优先级问题:如何让类型匹配优先于参数数量匹配@overload

我有一个基类_BaseThing,以及它的子类CubeThing和BallThing。_BaseThing类绝对不应该在程序中显式创建,所以它以_作为前缀。

我还有一个工厂类Thing,它会根据传入的thing_type创建特定的类(CubeThing或BallThing)。

重要的是,除了通用参数外,每个Thing的实现都有自己额外的独特参数(立方体有边长,球体有半径和some_other_unique_param)。

from __future__ import annotations
from typing import overload, Literal
from enum import Enum


class ThingType(Enum):
    CUBE = 0
    BALL = 1


class _BaseThing:
    """
    Base class for all Things.
    It shouldn't be used in real program, so it prefixed with _.
    """
    def __init__(self, thing_type: ThingType, color, material):
        self.type = thing_type
        self.color = color
        self.material = material

    def change_color(self, color): ...

    def change_material(self, material): ...


class CubeThing(_BaseThing):
    """
    Cubical implementation of the Thing.
    """
    def __init__(self, color, material, edge_length: float):
        super().__init__(ThingType.CUBE, color, material)
        self.edge_length = edge_length

    def some_cube_method(self):
        print('This is cube!')


class BallThing(_BaseThing):
    """
    Spherical implementation of the Thing.
    """
    def __init__(self, color, material, radius: float, some_other_unique_param: int):
        super().__init__(ThingType.BALL, color, material)
        self.radius = radius
        self.some_other_unique_param = some_other_unique_param

    def some_ball_method(self):
        print('This is ball!')


class Thing:
    """Class, that looks like base class for all Things (bc of the name), but it is actually a 'fabric'."""
    __thing_types_dict__ = {
        ThingType.CUBE: CubeThing,
        ThingType.BALL: BallThing,
    }
    @overload
    def __new__(cls, thing_type: Literal[ThingType.CUBE], color, material, edge_length: float) -> CubeThing:
        ...

    @overload
    def __new__(cls, thing_type: Literal[ThingType.BALL], color, material,
                radius: float, some_other_unique_param: int) -> BallThing:
        ...

    def __new__(cls, thing_type: ThingType, color, material, *args, **kwargs):
        if thing_type not in cls.__thing_types_dict__:
            raise TypeError('Unexpected thing_type.')
        
        return cls.__thing_types_dict__[thing_type](color, material, *args, **kwargs)

当我尝试调用ball = Thing(ThingType.BALL, 'blue', 'silicon', 1)时,PyCharm的静态分析器(以及pyright)会提示我传入了无效的thing_type参数——值得注意的是,这个例子里我忘记写最后一个参数了。也就是说,pyright已经“选中”了对应CubeThing的@overload。但只要我补上最后一个参数,它就会立刻“切换”到对应BallThing的@overload,警告也会消失。所以PyCharm/pyright并没有优先考虑我传入的类型,而是优先考虑传入的参数数量。

(示例场景:当参数数量不足时,分析器错误地匹配了Cube的重载,错误提示为thing_type类型无效)

不过,我希望只要输入第一个参数(thing_type),静态分析器就能立刻识别出正确的@overload,并基于它给出后续的警告。
也就是说,在上面的例子中,我希望它提示的不是传入了无效类型,而是传入的参数不足。

我原本以为问题出在我重载了__new__方法,所以我尝试了函数式的实现:

@overload
def create_thing(thing_type: Literal[ThingType.CUBE], color, material, edge_length: float) -> CubeThing:
    ...

@overload
def create_thing(thing_type: Literal[ThingType.BALL], color, material, 
                 radius: float, some_other_unique_param: int) -> BallThing:
    ...


def create_thing(thing_type: ThingType, color, material, *args, **kwargs):
    thing_types_dict = {
        ThingType.CUBE: CubeThing,
        ThingType.BALL: BallThing,
    }

    if thing_type not in thing_types_dict:
        raise TypeError('Unexpected thing_type.')

    return thing_types_dict[thing_type](color, material, *args, **kwargs)

但这个变体也有完全相同的问题。

(示例场景:函数式实现下,同样出现错误的类型匹配提示)

我还尝试在Literal参数和类型提示中使用字符串'0'和'1'来代替ThingType,但也没有用。


备注:内容来源于stack exchange,提问作者Gygabrain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 16:03:07