openlang

Почему внутри ClassVar нельзя использовать переменные типа?

Согласно PEP-526 (https://peps.python.org/pep-0526/#class-and-instance-variable-annotations)

Переменная типа не может находиться внутри аргумента ClassVar независимо от глубины вложенности: если T обозначает переменную типа, то недопустимы и ClassVar[T], и ClassVar[List[Set[T]]].

Почему существует такое ограничение? Сам PEP этого не объясняет; возможно, ответ есть где-нибудь в архивах Typing github…

Я столкнулся с этим, аннотируя обобщённый базовый класс с парой членов: атрибут экземпляра хранит экземпляр определённого типа, а атрибут класса хранит сам этот тип:

class _TransportBase[Conn: (httpx.Client, httpx.AsyncClient), Cred: httpx.Auth](ABC):
    base_url: str
    connection: Conn
    _credential: Cred
    _credential_type: ClassVar[Type]  # intended as ClassVar[Type[Cred]]

    @property
    def credential(self) -> Cred:
        return self._credential

    @credential.setter
    @abstractmethod
    def credential(self, incoming: Any):
        if not isinstance(incoming, self._credential_type):
            self._credential = self._credential_type(incoming)
        else:
            self._credential = incoming

Из-за такого ограничения ClassVar приходится выбирать между явным обозначением атрибута как принадлежащего классу и выражением связи его типа с типом _credential.

Это ранний набросок, который при доработке наверняка изменится. Буду рад предложениям, но сам вопрос здесь касается ClassVar.

Переведено с английскогоПоказать оригинал

Разговор продолжается 2

Следуя доводам
  1. Ссылка на ответ

    ClassVar принадлежит самому class, а не существует отдельно для каждого набора обобщённых параметров.

    Для другого примера представим реализацию обобщённого связного списка.

    @dataclass
    class Link[T]:
        item: T
        following: Self | None = None
    
    class Chain[T]:
        start: ClassVar[Link[T] | None] = None
    
        def __str__(self) -> str:
            owner = type(self)
            items = list[T]()
            cursor = owner.start
            while cursor:
                assert isinstance(cursor, Link)  # assist the typechecker
                items.append(cursor.item)
                cursor = cursor.following
            return f'{owner.__qualname__}({', '.join(map(repr, items))})'
    
    numbers = Chain[int]()
    words = Chain[str]()
    
    Chain[int].start = Link[int](3, Link[int](4))
    print(numbers)  # 3, 4
    Chain[str].start = Link[str]('hello', Link[str]('world'))
    print(words)  # 'hello', 'world'
    print(numbers)  # unexpectedly! 'hello', 'world'

    Список int и список str используют один и тот же .start. Он существует только в одном экземпляре.

    Поэтому, на мой взгляд, TypeVar внутри ClassVar не имеет особого смысла.

    Переведено с английскогоПоказать оригинал
  2. Ссылка на ответ

    Да, кажется, теперь я понимаю причину.

    Я хотел, чтобы реализации ABC переопределяли ClassVar более узким типом, а объявление в ABC служило главным образом документацией, поскольку на самом ABC у этого атрибута никогда не было бы настоящего значения.

    Ещё путало то, что authlib использует httpx2, а мой код использовал httpx, так что имена совпадали, но типы технически различались. После обновления этой зависимости удалось использовать базовый класс напрямую, вместо попытки параметризовать его в аннотации ClassVar.

    Переведено с английскогоПоказать оригинал

Другие стороны того же вопроса

  1. Как унаследовать класс внутри другого класса?

    Здравствуйте, В пакете, код которого я не могу менять, есть такая иерархия классов: Я хочу добавить удобную обёртку вокруг In…

    Ответы 1Последняя активность scala
  2. Сравнение привязок have и let в Lean4

    Я заметил, что эти два выражения и ведут себя одинаково. То же происходит при доказательстве теорем. Они взаимозаменяемы? В ч…

    Ответы 3Последняя активность lean-4
  3. Инициализация TypeScript Map со значениями в виде массивов строк

    Создаю Map, в котором каждая запись содержит массив строк. Как одновременно задать его тип и инициализировать одним выражение…

    Ответы 2Последняя активность typescript