How to have inherited type hints in python?
python type alias
python type hints keyword argument
python type hints inheritance
python type comments
python function type
python generic types
So my problem is That when I have a class of type
A that does things and I use those functions as a
subclass(B) they are still typed for class
A and do not accept my class
B object as arguments or as function signature.
My problem simplified:
from typing import TypeVar, Generic, Callable T = TypeVar('T') class Signal(Generic[T]): def connect(self, connector: Callable[[T], None]) -> None: pass def emit(self, payload: T): pass class A: def __init__(self) -> None: self.signal = Signal[A]() def do(self) -> None: self.signal.emit(self) def handle_b(b: "B") -> None: print(b.something) class B(A): def __init__(self) -> None: super().__init__() self.signal.connect(handle_b) @property def something(self) -> int: return 42
I can provide the complete signal class as well but that just distracts from the problem. This leaves me with one error in mypy:
error: Argument 1 to "connect" of "Signal" has incompatible type Callable[[B], None]; expected Callable[[A], None]
Since the signal handling is implemented in
A the subclass
B can't expect
B type objects to be returned even though it clearly should be fine...
The type hint error is entirely correct. You created a
Signal instance with
A as the type, in the
__init__ method of
self.signal = Signal[A]()
Passing in a subclass is fine, but all code interacting with that
Signal instance now has to work for
A instances only.
handle_b() on the other handrequires an instance of
B, and can't lower the requirement to
Drop the constraint:
self.signal = Signal()
or create an instance in each subclass with the correct type.
Subclass in type hinting, When you do cls: A , you're saying that cls is going to an instance of type A . To make it work with type or its subtypes use typing.Type . This module provides runtime support for type hints as specified by PEP 484, PEP 526, PEP 544, PEP 586, PEP 589, and PEP 591. The most fundamental support consists of the types Any, Union, Tuple, Callable, TypeVar, and Generic. For full specification please see PEP 484. For a simplified introduction to type hints see PEP 483.
from __future__ import annotations from typing import TypeVar, Generic, Callable T = TypeVar('T') class Signal(Generic[T]): def connect(self, connector: Callable[[T], None]) -> None: pass def emit(self, payload: T): pass class A(Generic[T]): def __init__(self) -> None: self.signal = Signal[T]() def do(self: A) -> None: self.signal.emit(self) def handle_b(b: B) -> None: print(b.something) class C: pass def handle_c(c: C) -> None: print(c) class B(A[B]): def __init__(self) -> None: super().__init__() self.signal.connect(handle_b) # OK self.signal.connect(handle_c) # incompatible type @property def something(self) -> int: return 42
typing — Support for type hints — Python 3.8.5 documentation, Generics. Since type information about objects kept in containers cannot be statically inferred in a generic way, abstract base classes have been� The type hints syntax has been pretty stable for a while now and in my opinion you should be using it, or at least have it on your radar to implement it when you can.
connector passed to
Signal[A] is of type
Callable[[A], None], which means it has to promise to be able to handle any instance of
A (or any of it's sub-classes).
handle_b cannot fulfill this promise, since it only works for instances of
B, it therefore cannot be used as a
connector for a
signal of type
connector of the
signal of any instance of
B will only ever be asked to handle an instance of
B, it therefore doesn't need to be of type
Signal[B] would be sufficient. This means the type of
signal is not fixed, but varies for different sub-classes of
A, this means
A needs to be generic.
The answer by ogurets correctly makes
A generic, however there is no a problem with
do, since it's unclear whether
self is of type expected by
self.signal.emit. We can promise that these types will always match by annotating
self with the same type variable used for
Signal. By using a new type variable
_A which is bound by
A, we tell mypy that
self will always be a subtype of
A and therefore has a property
from __future__ import annotations from collections.abc import Callable from typing import TypeVar, Generic T = TypeVar('T') class Signal(Generic[T]): def connect(self, connector: Callable[[T], None]) -> None: pass def emit(self, payload: T): print(payload) _A = TypeVar('_A', bound='A') class A(Generic[_A]): signal: Signal[_A] def __init__(self) -> None: self.signal = Signal[_A]() def do(self) -> None: self.signal.emit(self) def handle_b(b: "B") -> None: print(b.something) class B(A['B']): def __init__(self) -> None: super().__init__() self.signal.connect(handle_b) @property def something(self) -> int: return 42 b = B() reveal_type(b.signal) # Revealed type is '...Signal[...B*]'
PEP 484 -- Type Hints, So many of the examples have a dual purpose: show how to write the annotation, and show the inferred types. Variables�. Python 3.6 introduced a syntax for� By using the super() function, you do not have to use the name of the parent element, it will automatically inherit the methods and properties from its parent.
Type hints cheat sheet (Python 3) — Mypy 0.782 documentation, If you want to just get a quick glimpse of how type hints work in Python, and see This is done by inheriting from Protocol and defining the function signatures� Type comments cannot be used with the class-based syntax, for consistency with the class-based NamedTuple syntax. (Note that it would not be sufficient to support type comments for backwards compatibility with Python 2.7, since the class definition may have a total keyword argument, as discussed below, and this isn't valid syntax in Python 2.7.)
Python Type Checking (Guide) – Real Python, type consuming (I'm writing/annotating a subclass and have to copy potentially My idea here is to introduce some kind of marker (perhaps a @typing. inherit_type_hints untyped defs on derived classes python/mypy#3903. The official home of the Python Programming Language. Non-goals. While the proposed typing module will contain some building blocks for runtime type checking -- in particular the get_type_hints() function -- third party packages would have to be developed to implement specific runtime type checking functionality, for example using decorators or metaclasses.
Proposal: inheritance + annotations � Issue #269 � python/typing , Type hints make it trivial to find where a given class is used when you're trying to refactor your code base. While many IDEs already have some� Update 26 March 2017 This appears to have been fixed as of Python 3.5.3 and 3.6.0, so there's no longer a need to additionally inherit from collections.abc.Iterable. Getting type hints programmatically. If you have a need to, typing.get_type_hints() can be used to programmatically inspect type hints:
- Well, the error is correct, you did constrain
self.signal = Signal[A]().
- You basically set
T = Athere.
Bmay be a subclass, but because
A.somethingdoesn't exist, you can't use that attribute. You tied everything down to
Anow. A method that accepts
Bis obviously allowed to use
B.somethingso the base class
Acan't ever satisfy that requirement.
- shouldn't there be a way to tell the type Hinting to accept subtypes of A? I mean disabling the typing works, but then why have type hinting at all...
- Sorry, had to step away from the keyboard for a while; it probably is possible to use a
TypeVar()marked as covariant instead of
Abut won’t be able to test until tomorrow.
- I did already try that, I get a problem.py:10: error: Cannot use a covariant type variable as a parameter on the line: def emit(self, payload: T): And still have the old error :D maybe upgrade my mypy version...
- nice idea, but I still have errors on the emit of class A and the emit function definition itself, with mypy 0.600
- Hmm.. Last version of pip's mypy (0.5.0) looks fine. Sadly, too busy to dive deeper into this for now.