Тип подсказки условного вариационного применения - PullRequest
0 голосов
/ 27 февраля 2019

Я пытаюсь напечатать подсказку о частичном применении конструктора, который полностью применяется после предоставления экземпляра «tag».Это реализуется через класс-оболочку, который хранит конструктор и любые частично примененные аргументы.Поскольку оболочка предназначена для нескольких типов, она должна принимать переменную *args.

. Это создает два случая, которые отличаются аргументами, которые они принимают:

  1. Применить: (tag: Tag, ...) -> Cls
  2. Store: (...) -> Partial[Cls]

Примечательно, что в случае 2. может быть или не быть получен первый параметр.Оба пересекаются в своей арности, так как они разнообразны.Это просто реализовать.Я попытался напечатать эти подсказки, используя @overload:

from typing import TypeVar, Generic

#: the class to partially construct
Cls = TypeVar('Cls')

class Tag:
    """Instances of this class complete the partial application"""

class Partial(Generic[Cls]):
    """Partially construct ``ctor`` until a :py:class:`~.Tag` is applied"""
    def __init__(self, ctor: Type[Cls], *args):
        self.ctor = ctor
        self.args = args

    # type hints
    @overload
    def __call__(self, tag: Tag, *args) -> Cls:
        ...

    @overload
    def __call__(self, *args) -> 'Partial[Cls]':
        ...

    # implementation
    def __call__(self, *args):
        if args and isinstance(args[0], Tag):
            return self.ctor(args[0], *self.args, *args[1:])
        return Partial(self.ctor, *self.args, *args)

Однако ни mypy, ни PyCharm не довольны этим (PyCharm на данный момент нуждается в явном вызове метода, но это не моя проблема),Расширение второй перегрузки с явным не тегом (tag: Any, ...) -> Partial[Cls] не решает проблему.Оба инструмента либо сообщают о несоответствии типов, о несовместимой перегрузке, либо возвращаются к Any или Union.

. Любая помощь при подсказке типов это правильно.


Типпример кода проверки:

class VariadicString(str):
    def __new__(cls, *args):
        return str(args)


a = RecursivePartial(VariadicString, 1, 2, 3)
b = a(4, 5, 6)
c = b(Tag(), 7, 8, 9)
reveal_locals()  # absent for PyCharm

mypy правильно определяет типы a, b и c, но отклоняет программу из-за несовместимого перекрытия перегрузок:

test.py:17: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
test.py:38: error: Revealed local types are:
test.py:38: error: a: test.Partial[test.VariadicString*]
test.py:38: error: b: test,Partial[test.VariadicString*]
test.py:38: error: c: test.VariadicString*

PyCharm не отклоняет программу, но ошибочно определяет c как Union из обоих типов возврата:

a: Partial[VariadicString]
b: Partial[VariadicString]
c: Union[VariadicString, Partial[VariadicString]]

1 Ответ

0 голосов
/ 27 февраля 2019

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

Например, в нынешнем виде ваш код будет позволять передавать переменные Tag в переменные *args в любом случае, поэтому в подсказке типа нет ничего, что говорило бы пользователю, что вы не можете попробоватьсоздать частичный экземпляр, хранящий несколько переменных Tag.

Я не знаю, возможно ли это для вашего решения, но так как вы уже проверяете тип первой переменной args вручную, вы можете настаивать на том, чтобы метод вызывался либо сТег или явный None, изменяющий вторую перегрузку на:

@overload
    def __call__(self, tag: None, *args) -> 'Partial[Cls]':
        ...

и реализацию на:

# implementation
def __call__(self, *args):
    if args and isinstance(args[0], Tag):
        return self.ctor(args[0], *self.args, *args[1:])
    # Don't add the None to the args.
    return Partial(self.ctor, *self.args, *args[1:])
...