It sounds like you may be looking for something like the Protocol types proposed in PEP 544. That PEP isn't approved yet (and may not have a complete implementation yet either), so it may be a while before you get the feature you need (Python 3.8 at the earliest).
Anyway, according to the PEP, Protocols would let you describe a kind of abstract type based on what methods and attributes it has, without the concrete types needing to know about the Protocol or do anything in particular (it doesn't need to inherit from an abstract base class, just have the required methods). It's similar to how you can customize how isinstance
and issubclass
work using metaclasses, but it works with static type checking, not only at runtime.
For instance, iterators in Python are an existing protocol that many unrelated classes implement. If the PEP is approved and implemented, you won't need to declare a custom iterator type as inheriting from typing.Iterator
any more, it would figure it out automatically, just because the class has __iter__
and __next__
methods.
In your example, you could make an A_Like
protocol that requires a to_A
method:
class A_Like(typing.Protocol):
def to_A(self) -> A:
...
Then you'd implement A.to_A
with a simple return self
, while B.to_A
does the appropriate conversion. Both classes will be seen as matching the A_Like
protocol type, so def foo(a: A_Like)
would satisfy type checkers (with the body of the class needing to do a = a.to_A()
before calling any A
specific methods).
You can do this now with inheritance from a common abstract base class (which can be a simple mixin), but it's definitely not as elegant as it will be with Protocols. Another option if you don't have many classes you need to convert is to just use Union
types declarations: def foo(a: Union[A, B])