Общие типы и наследование Python: трудности с классами аннотирования, возвращающими взаимные экземпляры друг другаPython

Программы на Python
Ответить
Anonymous
 Общие типы и наследование Python: трудности с классами аннотирования, возвращающими взаимные экземпляры друг друга

Сообщение Anonymous »

Я пытаюсь аннотировать типы для некоторых классов и подклассов.
Вот ситуация до аннотаций. Проблема заключается в том, как правильно аннотировать getOther(), чтобы средства проверки статического типа правильно определяли тип возвращаемого значения для всех подклассов, без необходимости переопределять его для подклассов SubB1 и SubB2.
Кроме того, SubB1 и SubB2 должны оставаться подклассами из Б.

Код: Выделить всё

class A:
def __init__(self, a:int) -> None:
self.a:int = a

class SubA1(A): ...
class SubA2(A): ...

class B:
_otherClass = A

def __init__(self, b:int) -> None:
self.b:int = b

def getOther(self): #  type checker: SubA1
b2.getOther() # Returns SubA2(2) => type checker: SubA2
Я пробовал несколько подходов, но каждый по той или иной причине не удался:
1. Сделать B универсальным классом:

Код: Выделить всё

from typing import TypeVar, Generic

class A:
def __init__(self, a:int) -> None:
self.a:int = a

class SubA1(A): ...
class SubA2(A): ...

Aclass = TypeVar('Aclass')

class B(Generic[Aclass]):
_otherClass = A

def __init__(self, b:int) -> None:
self.b:int = b

def getOther(self) -> Aclass:
return self._otherClass(a = self.b)

class SubB1(B[SubA1]):
_otherClass = SubA1
class SubB2(B[SubA2]):
_otherClass = SubA2
Это работает для subb1 и subb2 , но не для самого b , поскольку нигде указано, что B должен работать с как конкретный тип ( на самом деле по-прежнему является универсальным классом):

Код: Выделить всё

b0 = B(0)
b1 = SubB1(1)
b2 = SubB2(2)

_ = b0.getOther() # Type checker => Any, Not OK
_ = b1.getOther() # Type checker => SubA1, OK
_ = b2.getOther() # Type checker => SubA2, OK
2. Отдельный универсальный класс для getOther
Вторая попытка состояла в том, чтобы создать отдельный универсальный класс для хранения метода getOther и наследовать его от других классов. из него с помощью множественного наследования:

Код: Выделить всё

...

class _GenericB(Generic[Aclass]):
def getOther(self) -> Aclass:
return self._otherClass(a = self.b)

class B(_GenericB[A]):
_otherClass = A

def __init__(self, b:int) -> None:
self.b:int = b

class SubB1(B, _GenericB[SubA1])
class SubB1(B, _GenericB[SubA2])
Теперь это работает для B, но не для SubB1 и SubB2, поскольку множественное наследование таково, что вызывается getOther SubB1 и SubB2 преобразуются в метод, определенный в B, который помечен буквой A.

Код: Выделить всё

b0 = B(0)
b1 = SubB1(1)
b2 = SubB2(2)

_ = b0.getOther() # Type checker => A, OK
_ = b1.getOther() # Type checker => A, Not OK
_ = b2.getOther() # Type checker => A, Not OK
Я также попытался вернуть порядок b и _genericb в subb1 и subb2 , надеясь получить правильное разрешение GetTher , разрешение MRO не удается: (

Код: Выделить всё

TypeError: Cannot create a consistent method resolution order (MRO) for bases _GenericB, B'
):

Код: Выделить всё

...
class SubB1(_GenericB[SubA1], B) # B after _GenericB raises MRO resolution TypeError
class SubB1(_GenericB[SubA2], B) # B after _GenericB raises MRO resolution TypeError
< /code>
[b] 3. Отдельные базовые классы [/b] 
Третья попытка, которую я предпринял, - это разделить общую и не общая часть класса B 
на отдельные базовые классы, имея b , Subb1 и subb2 наследуют от них.
Это, однако, нарушает отношения наследования:

Код: Выделить всё

...

class _NongenericB:
def __init__(self, b:int) -> None:
self.b:int = b

class _GenericB(Generic[Aclass]):
def getOther(self) -> Aclass:
return self._otherClass(a = self.b)

class B(_NongenericB, _GenericB[A]):
_otherClass = A

class SubB1(_NongenericB, _GenericB[SubA1]):
_otherClass = SubA1

class SubB2(_NongenericB, _GenericB[SubA2]):
_otherClass = SubA2
< /code>
Однако: < /p>
_ = b0.getOther() # Type checker => A, OK
_ = b1.getOther() # Type checker => SubA1, OK
_ = b2.getOther() # Type checker => SubA2, OK

assert isinstance(b0, B) # OK
assert isinstance(b1, B) # Assertion error
assert isinstance(b2, B) # Assertion error
Кроме того, я не могу добавить B к базовым классам SubB1 и SubB2, потому что попадаю в ту же ситуацию, что и пункт 2.
p>

Я здесь немного растерян. Что мне делать?
Одним из очевидных решений было бы переопределить метод в подклассах и аннотировать сигнатуры вручную, без использования дженериков, но я бы хотел этого избежать, если возможно, поскольку с этим механизмом может работать множество методов.
Идеальным решением было бы раз и навсегда определить метод в базовом классе.
Есть идеи? p>
Спасибо!

Подробнее здесь: https://stackoverflow.com/questions/793 ... rning-reci
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»