I'm doing some experiments with typing in Python 3.6 and mypy. I want to design an entity class that can be instantiated in two ways:
p = Person(name='Hannes', age=27)
)p = Person.from_state(person_state)
).The Entity
class, from which Person
derives, has the state class as a generic parameter. However, when validating the code with mypy, I receive an error that Person.from_state
doesn't pick up the state type from the class it inherits from:
untitled2.py:47: error: Argument 1 to "from_state" of "Entity" has incompatible type "UserState"; expected "StateType"
I thought that by inheriting from Entity[UserState]
, StateType
would be bound to UserState
and the method signatures in the child classes would update accordingly.
This is the full code. I have marked the line where I suspect I'm doing things wrong with ?????
. Line 47 is almost at the bottom and marked in the code.
from typing import TypeVar, Generic, NamedTuple, List, NewType
EntityId = NewType('EntityId', str)
StateType = TypeVar('StateType')
class Entity(Generic[StateType]):
id: EntityId = None
state: StateType = None
@classmethod
def from_state(cls, state: StateType): # ?????
ret = object.__new__(cls)
ret.id = None
ret.state = state
return ret
def assign_id(self, id: EntityId) -> None:
self.id = id
class UserState(NamedTuple):
name: str
age: int
class User(Entity[UserState]):
def __init__(self, name, age) -> None:
super().__init__()
self.state = UserState(name=name, age=age)
@property
def name(self) -> str:
return self.state.name
@property
def age(self) -> int:
return self.state.age
def have_birthday(self) -> None:
new_age = self.state.age+1
self.state = self.state._replace(age=new_age)
# Create first object with constructor
u1 = User(name='Anders', age=47)
# Create second object from state
user_state = UserState(name='Hannes', age=27)
u2 = User.from_state(user_state) # Line 47
print(u1.state)
print(u2.state)
This was a bug in mypy that was fixed in mypy 0.700. As several people in the comments noted, that line of code validates fine in newer versions.
Note that in newer versions of mypy, the code in the question has a different problem:
main.py:8: error: Incompatible types in assignment (expression has type "None", variable has type "EntityId")
main.py:9: error: Incompatible types in assignment (expression has type "None", variable has type "StateType")
But that's outside the scope of the question and up to you to resolve however you'd like.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With