【问题标题】:Inherit generic type in python 3 with typing用打字继承python 3中的泛型类型
【发布时间】:2018-07-16 09:09:37
【问题描述】:

我正在用 Python 3.6 和 mypy 做一些实验。我想设计一个可以通过两种方式实例化的实体类:

  • 通过使用普通初始化程序 (p = Person(name='Hannes', age=27))
  • 静态来自状态对象 (p = Person.from_state(person_state))。

Entity 类(Person 派生自该类)将状态类作为通用参数。但是,在使用 mypy 验证代码时,我收到一个错误:Person.from_state 没有从它继承的类中获取状态类型:

untitled2.py:47:错误:“Entity”的“from_state”的参数 1 具有不兼容的类型“UserState”;预期的“StateType”

我认为通过从Entity[UserState] 继承,StateType 将绑定到UserState,并且子类中的方法签名会相应更新。

这是完整的代码。我已经标记了我怀疑我用????? 做错事的那一行。第 47 行几乎在底部,并在代码中进行了标记。

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)

【问题讨论】:

  • 这是一些学术研究,还是您正在解决一个真正的问题?仅仅因为数据模型看起来有点奇怪而询问
  • 这不是生产系统意义上的“真正问题”。我正在尝试在 python 中使用这种方法是否可行:vaughnvernon.co/?p=879。但是,至于泛型,我真的很想知道它是如何工作的以及我做错了什么。
  • 有趣的是,此错误在 Python 3.6.10 或 3.8.6 和 mypy 0.790 中无法重现。 @HannesPetri 您使用的是哪个版本的 mypy?
  • python 3.9 给了我UserState(name='Anders', age=47) UserState(name='Hannes', age=27)

标签: python generics type-hinting mypy python-typing


【解决方案1】:

我知道这个问题有点老了,但仅供将来参考:

from __future__ import annotations
from typing import NamedTuple, Optional, Type


class UserState(NamedTuple):
    name: str
    age: int


class Entity:
    id: Optional[str]
    state: UserState

    @classmethod
    def from_state(cls: Type[Entity], state: UserState) -> Entity:
        entity_from_state: Entity = object.__new__(cls)
        entity_from_state.id = None
        entity_from_state.state = state
        return entity_from_state

    def assign_id(self, id: str) -> None:
        self.id = id


class User(Entity):
    def __init__(self, name: str, age: int) -> None:
        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)

【讨论】:

    【解决方案2】:

    这是 mypy 中的一个错误,在 mypy 0.700 中是 fixed。正如 cmets 中的一些人所指出的,这行代码在新版本中验证良好。

    请注意,在较新版本的 mypy 中,问题中的代码存在不同的问题:

    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")
    

    但这超出了问题的范围,您可以随意解决。

    【讨论】:

      猜你喜欢
      • 2018-09-27
      • 2015-09-15
      • 2018-08-01
      • 1970-01-01
      • 2020-01-05
      • 2021-10-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多