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

