I have a method that receives objects that share a common superclass but are of many different subtypes. It checks the type of each object, and, depending on the type, passes it to different functions. The receiver functions are annotated to receive specific subtypes.
The object types are known ahead of time and are part of a finite set. Objects will only ever be depth-1 subclasses, not sub-subclasses.
This code is very performance critical. To avoid the overhead of calling isinstance
multiple times when dispatching, I store the type in a variable.
However, I'm having trouble getting PyCharm to detect the "specialization" of each object based on that stored type variable. It indicates typeerrors because I'm passing objects whose type it only knows as the superclass to functions that receive specific subclasses.
For example, my code looks like this:
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
With that code, PyCharm detects type mismatches on the calls to receiver1
and receiver2
.
Ordinarily, I'd give up here, since Python's a dynamically typed language and it seems unreasonable to insist that an IDE structurally understand code that dispatches on dynamic type.
However, PyCharm can do that specialization; it just only seems to work with isinstance
checks. If I replace dispatch
with the below code, PyCharm type checks it correctly:
def dispatch():
item = get_item()
if isinstance(item, Subtype1):
receiver1(item)
elif isinstance(item, Subtype2):
receiver2(item)
# ... many more cases
The problem is, that's too slow. isinstance
itself is much slower than an is
comparison, and I have to call it many times to check each case; there are many cases.
Is there a way to make PyCharm detect simple specialization types without using isinstance
? If so, how?
What I've tried
- Ordinarily, this would be a good fit for object orientation. If the
receiver
functions were instead methods on each subtype, I could simply callitem.recieve()
or something each time. However, the subtypes are declared in code I don't control (and are record classes implemented in C, so they can't be subclassed--weird, I know, but I did disclaim that it's performance-critical code :)). - Various dispatch table approaches using a
dict
keyed by type with receiver methods as values similarly defeat PyCharm's type checking.
CodePudding user response:
You could give additional type hints to 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)
This should not affect performance of the code.
Another approach would be to inject the receiver method into Subtype1/2 classes:
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()