Я хочу тип указывает из Subclass.run на применение к сайтам вызова его метода __call__.
Я хотел бы определить дочерние классы (т.е. 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`)
Код: Выделить всё
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
Я открыт для любых более простых/сложных решений и других версий Python (используется 3.11.10).
Подробнее здесь: https://stackoverflow.com/questions/791 ... -type-hint
Мобильная версия