Let's say I want to do define wrapper classes on sets and lists that add some useful methods, like this:
from abc import ABC
class AbstractGizmo(ABC):
def bloviate(self):
print(f"Let me tell you more about my {len(self)} elements")
class ListGizmo(list, AbstractGizmo):
pass
class SetGizmo(set, AbstractGizmo):
pass
Now I can call:
>>> ListGizmo([1, 2, 3]).bloviate()
>>> SetGizmo({1, 2, 3}).bloviate()
But I also want to have bloviate()
available on its own as a utility method:
from typing import Union, Set, List
def bloviate(collection: Union[Set, List]):
print(f"Let me tell you more about my {len(collection)} elements")
class AbstractGizmo(ABC):
def bloviate(self):
return bloviate(self)
So I can also do:
>>> bloviate([1, 2, 3])
>>> bloviate({1, 2, 3})
Since subclass ListGizmo
is a list, and subclass SetGizmo
is a set, this setup actually works fine in practice. But static type checkers (like pyright) don't know that, so they (correctly) show an error here:
class AbstractGizmo(ABC):
def bloviate(self):
return bloviate(self) # Error: Type 'AbstractGizmo' cannot be assigned
# to type 'Set[Unknown] | List[Unknown]'
Is there some way I can indicate to Python / pyright that, essentially, "all instances of AbstractGizmo
are guaranteed to be in Union[Set, List]
"? This syntax escapes me.
(Note that of course in this simple example I can just define bloviate()
on each subclass to avoid the problem. In reality I have more methods and more wrapper subclasses, so I get a combinatorial explosion if I can't abstract them to AbstractGizmo
.)