如何在不使用 "isinstance" 的情况下告诉 PyCharm 一个对象是一个类型的特化(子类)?

How can I tell PyCharm that an object is a specialization (subclass) of a type without using "isinstance"?

我有一个方法可以接收共享一个公共超类但具有许多不同子类型的对象。它检查每个对象的类型,并根据类型将其传递给不同的函数。接收器函数被注释为接收特定的子类型。

对象类型是提前知道的并且是有限集的一部分。对象只会是 depth-1 sub类,而不是 sub-sub类.

此代码对性能非常关键。为了避免在调度时多次调用 isinstance 的开销,我将类型存储在一个变量中。

但是,我无法让 PyCharm 根据存储的类型变量检测每个对象的“专业化”。它指示类型错误,因为我将其类型仅作为超类知道的对象传递给接收特定 sub类.

的函数

例如,我的代码如下所示:

class Superclass: ...
class Subtype1(Superclass): ...
class Subtype2(Superclass): ...
# ...many more subtypes

def receiver1(arg: Subtype1): ...
def receiver2(arg: Subtype2): ...
# ...many more receivers

def dispatch():
    item = get_item()
    item_type = type(item)
    if item_type is Subtype1:
        receiver(item)
    elif item_type is Subtype2:
        receiver2(item)
    # ... many more cases

使用该代码,PyCharm 检测到对 receiver1receiver2 的调用的类型不匹配。

通常,我会在这里放弃,因为 Python 是一种动态类型的语言,坚持 IDE 在结构上理解调度动态类型的代码似乎是不合理的。

但是,PyCharm 可以进行该专业化;它似乎只适用于 isinstance 检查。如果我用下面的代码替换 dispatch,PyCharm 类型检查正确:

def dispatch():
    item = get_item()
    if isinstance(item, Subtype1):
        receiver1(item)
    elif isinstance(item, Subtype2):
        receiver2(item)
    # ... many more cases

问题是,那太慢了。 isinstance 本身比 is 比较慢得多,我必须多次调用它来检查每个案例;有很多情况。

有没有办法让 PyCharm 在不使用 isinstance 的情况下检测简单的专业化类型?如果是,怎么做?

我试过的

您可以为 PyCharm

提供额外的类型提示
def dispatch():
    item = get_item()
    item_type = type(item)
    if item_type is Subtype1:
        item: Subtype1
        receiver1(item)
    elif item_type is Subtype2:
        item: Subtype2
        receiver2(item)

这应该不会影响代码的性能。

另一种方法是将接收器方法注入 Subtype1/2 类:

def receiver1(arg: Subtype1):
    print("receiver1 called")

def receiver2(arg: Subtype2):
    print("receiver2 called")

class MyMeta(type):
    pass

d = dict(Subtype1.__dict__)
d.update({"receiver": receiver1})
Subtype1 = MyMeta(Subtype1.__name__,
                  Subtype1.__bases__,
                  d)

d = dict(Subtype2.__dict__)
d.update({"receiver": receiver2})
Subtype2 = MyMeta(Subtype2.__name__,
                  Subtype2.__bases__,
                  d)

def dispatch():
    item = get_item()
    item.receiver()