Метод класса-обертки с правильной подсказкой типаPython

Программы на Python
Ответить
Anonymous
 Метод класса-обертки с правильной подсказкой типа

Сообщение Anonymous »

Как я хочу определить дочерние классы (т. е. MyClass сейчас):

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

class ParentClass:
# This class can contain the nasty definitions necessary
#   to keep child classes nice looking
@abstractmethod
def run(self):
raise NotImplementedError()

class MyClass(ParentClass):
# I don't want to use `metaclass=MetaClass` here

# Define the logic in the method `run(...)`,
#   signature can vary in different child classes
# Trying to avoid the need to use the `__call__` dunder
def run(self, a: int = 5) -> int:
print("my", a)
return 1

class MyClass2(ParentClass):
def run(self, b: str = "", c: bool = False) -> int:
print("my2", b, c)
return 2
Как я хочу использовать дочерние классы:

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

my = MyClass()
my(a=6) # prints `my 6` and returns `1`

my2 = MyClass2()
my2(b="example", c=True) # prints `my2 example True` and returns `2`
Моя причина вышесказанного — обернуть методы запуска и выполнять действия до и после его вызова.
Я пробовал следующее:

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

from abc import abstractmethod
from functools import wraps
from typing import Callable, TypeVar, ParamSpec, Self, Any, cast
from __future__ import annotations

P = ParamSpec("P")
T = TypeVar("T")

class ParentMeta(type):

def __new__(cls: Self, name: str, bases: tuple, namespace: dict[str, Any]) -> ParentMeta:

def wrapper(func: Callable[P, T]) -> Callable[P, T]:
@wraps(func)
def inner(self, *args: P.args, **kwargs: P.kwargs) -> T:
print("--pre--")
ret = func(self, *args, **kwargs)
print("--post--")
return ret
return inner

if "run" in namespace:
namespace["__call__"] = wrapper(namespace["run"])
del namespace["run"]

return cast(ParentMeta, super().__new__(cls, name, bases, namespace))

class ParentClass(object, metaclass=ParentMeta):
@abstractmethod
def run(self):
raise NotImplementedError()

class MyClass(ParentClass):
def run(self, a: int = 5) -> int:
print("my", a)
return 1

my = MyClass()
my(a=6)
# the problem here is that I lost the parameter/type hint in the IDE (I'm using VSCode)
# (so when I'm writing `my(`, I cannot see that it has an argument `a`)
Если я откажусь от выполнения и определю __call__ (чего мне действительно не хочется), я получу подсказку типа (поскольку она решена из MyClass):

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

from abc import abstractmethod
from functools import wraps
from typing import Callable, TypeVar, ParamSpec, Self, Any, cast
from __future__ import annotations

P = ParamSpec("P")
T = TypeVar("T")

class ParentMeta(type):

def __new__(cls: Self, name: str, bases: tuple, namespace: dict[str, Any]) -> ParentMeta:

def wrapper(func: Callable[P, T]) -> Callable[P, T]:
@wraps(func)
def inner(self, *args: P.args, **kwargs: P.kwargs) -> T:
print("--pre--")
ret = func(self, *args, **kwargs)
print("--post--")
return ret
return inner

if "__call__" in namespace:
namespace["__call__"] = wrapper(namespace["__call__"])

return cast(ParentMeta, super().__new__(cls, name, bases, namespace))

class ParentClass(object, metaclass=ParentMeta):
@abstractmethod
def __call__(self):
raise NotImplementedError()

class MyClass(ParentClass):
def __call__(self, a: int = 5) -> int:
print("my", a)
return 1

my = MyClass()
my(a=6) # got the hint `(a: int = 5) -> int` when typed
Могу ли я написать вышеизложенное таким образом, чтобы мне не приходилось использовать __call__ в MyClass и при этом получать правильную подсказку о типе использования?
Я открыт для любых более простых/сложных решений и других версий Python (используется 3.11.10).


Подробнее здесь: https://stackoverflow.com/questions/791 ... -type-hint
Ответить

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

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

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

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

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