У меня есть универсальный c класс (скажем, Automata[S]
), и я бы хотел, чтобы другой не универсальный c класс (скажем, Test
) имел Automata[S]
в качестве поля для некоторого S
( т.е. я хотел бы иметь ∃S. Automata[S]
в качестве поля).
Моя первая попытка - использовать неподписанный тип Automata
для аннотирования поля следующим образом. Но применение delta
и is_finale
не является безопасным от типа, поскольку параметр типа S
становится Any
.
import abc
from typing import Generic, TypeVar
S = TypeVar('S')
class Automata(Generic[S], metaclass=abc.ABCMeta):
@abc.abstractmethod
def initial_state(self) -> S:
raise NotImplementedError()
@abc.abstractmethod
def delta(self, state: S, x: int) -> S:
raise NotImplementedError()
@abc.abstractmethod
def is_final(self, state: S) -> bool:
raise NotImplementedError()
class Counter(Automata[int]):
def initial_state(self) -> int:
return 0
def delta(self, state: int, x: int) -> int:
return state + x
def is_final(self, state: int) -> bool:
return state >= 10
class Test:
automata: Automata
def __init__(self, automata: Automata[S]):
self.automata = automata
def test(self) -> bool:
s0 = self.automata.initial_state()
# reveal_type(self.automata) #=> Automata[Any]
# reveal_type(s0) #=> Any
s1 = self.automata.delta(s0, 1)
s2 = self.automata.delta(s1, 2)
s3 = self.automata.delta(s2, 3)
return self.automata.is_final(s3)
test = Test(Counter())
test.test()
Моя другая попытка состоит в использовании изоморфизма ∃X. F(X) ≅ ∀R. (∀X. F(X) → R) → R
следующим образом. Выглядит как безопасный для типов, так как раскрываемый тип - это не Any
, но подкласс Cont
для продолжения записи каждый раз утомителен и немного сложен.
import abc
from typing import Generic, TypeVar
S = TypeVar('S')
R = TypeVar('R')
class Automata(Generic[S], metaclass=abc.ABCMeta):
@abc.abstractmethod
def initial_state(self) -> S:
raise NotImplementedError()
@abc.abstractmethod
def delta(self, state: S, x: int) -> S:
raise NotImplementedError()
@abc.abstractmethod
def is_final(self, state: S) -> bool:
raise NotImplementedError()
class Counter(Automata[int]):
def initial_state(self) -> int:
return 0
def delta(self, state: int, x: int) -> int:
return state + x
def is_final(self, state: int) -> bool:
return state >= 10
class Cont(Generic[R], metaclass=abc.ABCMeta):
@abc.abstractmethod
def __call__(self, automata: Automata[S]) -> R:
raise NotImplementedError()
class EAutomata(metaclass=abc.ABCMeta):
"""
∃S. Automata[S]
"""
@abc.abstractmethod
def unpack(self, cont: Cont[R]) -> R:
raise NotImplementedError()
@staticmethod
def pack(automata: Automata[S]) -> 'EAutomata':
return EAutomataBody[S](automata)
class EAutomataBody(EAutomata, Generic[S]):
def __init__(self, automata: Automata[S]):
self._automata = automata
def unpack(self, cont: Cont[R]) -> R:
return cont(self._automata)
class Test:
automata: EAutomata
def __init__(self, automata: Automata[S]):
self.automata = EAutomata.pack(automata)
def test(self) -> bool:
class C(Cont[bool]):
def __call__(self, automata: Automata[S]) -> bool:
s0 = automata.initial_state()
# reveal_type(automata) #=> Automata[S`-1]
# reveal_type(s0) #=> S`-1
s1 = automata.delta(s0, 1)
s2 = automata.delta(s1, 2)
s3 = automata.delta(s2, 3)
return automata.is_final(s3)
return self.automata.unpack(C())
test = Test(Counter())
test.test()
Есть ли способ упростить вышеуказанный способ? , или есть лучший способ использовать экзистенциальные типы в Python?