Я хочу ввести декоратор, то есть вызываемый объект, который возвращает вызываемый объект.
Я хочу ввести это таким образом, чтобы в момент оформления проверять правильность аргументов и типа возвращаемого значения, а также проверять в момент вызова.
Это можно исправить, добавив метод __get_ в ProtocolCallable, но мне нужно снова определить аргументы и ответ, и это может быть очень многословно, если таковые имеются. много аргументов.
Это пример, который работает во время выполнения, но не удается вызвать my_method на mypy (аргумент 1 имеет несовместимый тип «Callable[[MyClass, int], Coroutine[Any, Any, str]]»; ожидается «MethodCallable)
from typing import Any, Awaitable, Callable, Protocol
class MethodCallable(Protocol):
def __call__(self, some_arg: int) -> Awaitable[str]: ...
def some_decorator() -> Callable[[MethodCallable], MethodCallable]:
def wrapper(func: MethodCallable) -> MethodCallable:
print("Decorator is applied.")
return func
return wrapper
class MyClass:
@some_decorator()
async def my_method(self, some_arg: int) -> str:
return f"Result: {some_arg}"
# Example usage
if __name__ == "__main__":
obj = MyClass()
print(obj.my_method(some_arg=1)) # Output: Result: 1
Я хочу, чтобы mypy предупреждал, если я это сделаю:
obj.my_method("str") # input should be int
и
class MyClass:
@some_decorator()
async def my_method(self, some_arg: str) -> str: # some_arg should be int
Исправление может быть следующим:
class MethodCallable(Protocol):
def __call__(self, __real_self: Any, __some_arg: int) -> Awaitable[str]: ...
def __get__(self, instance: Any, owner: Any) -> "Callable[[int], Awaitable[str]]": ...
но для этого потребуется определить в два раза больше аргументов и ответа, и не существует одинаковых проверок, поэтому это пройдет mypy и завершится ошибкой во время выполнения.
from typing import Any, Awaitable, Callable, Protocol
class MethodCallable(Protocol):
def __call__(self, __real_self: Any, __some_arg: int) -> Awaitable[str]: ...
def __get__(self, instance: Any, owner: Any) -> "Callable[[int], Awaitable[str]]": ...
def some_decorator() -> Callable[[MethodCallable], MethodCallable]:
def wrapper(func: MethodCallable) -> MethodCallable:
print("Decorator is applied.")
return func
return wrapper
class MyClass:
@some_decorator()
async def my_method(self, some_arg: int) -> str:
return f"Result: {some_arg}"
# Example usage
if __name__ == "__main__":
obj = MyClass()
print(obj.my_method(1, "a")) # Output: Result: 1