Определите подсказки по типу переменных параметров для вызываемых объектов в PythonPython

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Определите подсказки по типу переменных параметров для вызываемых объектов в Python

Сообщение Anonymous »

У меня есть класс, который может содержать функцию, которая будет вызываться где-то еще. В зависимости от того, что его вызывает, оно может иметь переменное количество аргументов. Один владелец может вызывать его с аргументами (event, int, str), а другой может вызывать его с (event, str, bool, dict). Позже выполняются проверки, чтобы гарантировать, что подпись соответствует тому, что необходимо владельцу. В целях подсказки типов мне нужно убедиться, что передаваемая подпись соответствует всему, что начинается с нашего объекта Event, и всему, что после этого, все в порядке. В результате такие функции, как def foo(event: Event, a: int, b: bool) и def (event: ClickEvent, c: dict, *args, **kwargs) -> typing.Coroutine[Any , Any, str] абсолютно допустимы.
В следующем примере:

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

from typing import *
from dataclasses import dataclass

PARAMS = ParamSpec("PARAMS")

@dataclass
class Event:
field1: int
field2: bool
name: str

def get_name(self) -> str:
return self.name

class SomeCaller:
...

class Element:
...

class ClickEvent(Event):
def __init__(self, field1: int, field2: int, target: str):
super().__init__(field1=field1, field2=field2, name="click")
self.__target = target

@property
def target(self) -> str:
return self.__target

# The type hint in question
HANDLER = Callable[
[
Event,
PARAMS
], Union[Any, Coroutine]
]

def control(event: Event, *args, **kwargs) -> bool:
pass

def false_control(event: ClickEvent, *args, **kwargs) -> bool:
pass

async def async_function(event: Event, arg1: int, arg2: int, *args, **kwargs):
return 9

def function(event: ClickEvent, arg1: int, arg2: int, *args, **kwargs):
return 7

def other_function(event: Event, caller: SomeCaller, element: Element):
return 8

class EventHandlerWhatsit:
def __init__(self, handler: HANDLER):
self.__handler = handler

control_value = EventHandlerWhatsit(control)
false_control_value = EventHandlerWhatsit(false_control)
async_function_value = EventHandlerWhatsit(async_function)
function_value = EventHandlerWhatsit(function)
other_function_value = EventHandlerWhatsit(other_function)

def main():
print("This works")

if __name__ == "__main__":
main()
Предупреждения о подсказках при вводе появляются в объявлении false_control_value, async_function_value, function_value иother_function_value, все с предупреждениями типа «Ожидаемый тип» '(Event, ParamSpec("PARAMS")) -> Сопрограмма | Any' (соответствует общему типу '(Event, ParamSpec("PARAMS")) -> Coroutine | Any'), got '(event: Event, arg1: int, arg2: int, args: tuple[Any, ...] , kwargs: dict[str, Any]) -> Any' вместо . Объявление control_value не представляет проблемы. Присвоение self.__handler = handler в инициализаторе EventHandlerWhatsit также отображает предупреждение ожидаемого типа '(Event, ParamSpec("PARAMS")) -> Coroutine | Any', got '(Event, ParamSpec("PARAMS")) -> Coroutine | Any' вместо , что мне кажется странным.
Все, что ему нужно указать, это «Что-то, что можно вызывать, если оно начинается с параметра, который является подклассом Event ». Имена не имеют значения. Я экспериментировал с определением HANDLER разными способами, например, определял *args и **kwargs как Tuple[Any, ...] и Dict[str, Any] (с необязательным и без него), с дополнительными параметрами и по-прежнему выдает те же виды предупреждений.
Я' Я застрял в Python 3.8 и редактирую в PyCharm, который показывает предупреждения.
Есть идеи?
РЕДАКТИРОВАТЬ >:
@Daniil Fajnberg и @SUTerliakov дали отличные ответы в комментариях:

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

HANDLER = Callable[
Concatenate[
Event,
PARAMS
], Union[Any, Coroutine]
]

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

Concatenate
позволяет аннотации сопоставлять значения, немного отличающиеся от тех, которые указаны во входном определении.
Возьмите следующий недопустимый код:

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

def test_inner(arg: Callable[[str, int, P], Any]):
pass

def test_input(i: str, j: int, q: str = None, *args, **kwargs):
pass

def test_outer():
test_inner(test_input)
Линтер выдаст предупреждение, поскольку наличие необязательного параметра q не соответствует ожиданию параметра arg в test_inner. Однако измените определение arg в test_inner, чтобы оно выглядело как arg: Callable[Concatenate[str, int, P], Any], и линтер в порядке.
Предупреждение: ParamSpec и Concatenate были представлены в Python 3.10. Если вам необходимо использовать более старую версию из-за ограничений среды, используйте пакет typing_extensions, чтобы обеспечить эту функциональность.


Подробнее здесь: https://stackoverflow.com/questions/767 ... -in-python
Реклама
Ответить Пред. темаСлед. тема

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

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

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

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

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

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