Issue
I would like to exclusively type hint an argument to a specific class but exclude any subclasses.
class A:
pass
class B(A):
pass
def foo(obj: A):
pass
foo(B()) # I'd like the type checker to warn me here that it expects A, not B
Is this possible? and if so, how?
(bonus points if you can tell me what I would call this. Googling wasn't helpful, but I'm afraid I'm using the wrong terminology to describe this)
Solution
No, this is not possible to do.
Fundamentally, the Python typing ecosystem assumes that you are following the Liskov substitution principle -- assumes that it is always safe to substitute a subclass in places designed to handle the parent.
The fact that it permits you to pass in instances of B in addition to instances of A in your code snippet is just one example of this principle in play.
So if your subclass B is designed not to follow the Liskov substitution principle, that probably it wasn't ever really a "kind of" A to begin with and shouldn't be subclassing it.
You could fix this by either adjusting your code so B does properly follow Liskov or by making B stop subclassing A and instead use composition instead of inheritance as a mechanism for code reuse. That is, make B keep an instance of A as a field and use it as appropriate.
And if you run into a rare case where it's legitimately not possible to ever subclass A without breaking Liskov, something you could do to prevent people from accidentally subclassing it would be to explicitly mark A as being final:
from typing import final
# If you want to support Python 3.7 or earlier, pip-install 'typing_extensions'
# and do 'from typing_extensions import final' instead
@final
class A: pass
class B(A): pass
This should make your type checker report a "B cannot subclass A" error on the definition of B. And if you fix that error by changing/deleting B
, the call to foo(B())
should also naturally fail to type-check.
Answered By - Michael0x2a
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.