Я начал с написания класса Loggable с методом-оболочкой это позволяет мне украшать методы подклассов и записывать их вызовы
Код: Выделить всё
Param = ParamSpec("Param")
RetType = TypeVar("RetType")
CountType = TypeVar("CountType", bound="FunctionCount")
class FunctionCount(Generic[CountType]):
def __init__(self, count_dict: dict[str, int]) -> None:
self.count_dict = count_dict
@staticmethod
def count(
func: Callable[Concatenate[CountType, Param], RetType],
) -> Callable[Concatenate[CountType, Param], RetType]:
def wrapper(
self: CountType, *args: Param.args, **kwargs: Param.kwargs
) -> RetType:
function_name = f"{self.__class__.__name__}.{func.__name__}"
if function_name not in self.count_dict:
self.count_dict[function_name] = 0
self.count_dict[function_name] += 1
return func(self, *args, **kwargs)
return wrapper
Код: Выделить всё
class A(FunctionCount):
def __init__(self, count_dict: dict[str, int]) -> None:
super().__init__(count_dict)
@FunctionCount.count
def func(self) -> None:
pass
@FunctionCount.count
def func2(self) -> None:
pass
count_dict: dict[str, int] = {}
a = A(count_dict)
a.func()
a.func()
a.func2()
print(count_dict)
assert count_dict == {"A.func": 2, "A.func2": 1}
Но потом я подумал, что было бы неплохо иметь собственные имена для методов, поэтому я заменил обертку на декоратор
Код: Выделить всё
class FunctionCount(Generic[CountType]):
def __init__(self, count_dict: dict[str, int]) -> None:
self.count_dict = count_dict
@staticmethod
def count(
f_name: str | None = None,
) -> Callable[
[Callable[Concatenate[CountType, Param], RetType]],
Callable[Concatenate[CountType, Param], RetType],
]:
def decorator(
func: Callable[Concatenate[CountType, Param], RetType],
) -> Callable[Concatenate[CountType, Param], RetType]:
def wrapper(
self: CountType, *args: Param.args, **kwargs: Param.kwargs
) -> RetType:
function_name = f_name or f"{self.__class__.__name__}.{func.__name__}"
if function_name not in self.count_dict:
self.count_dict[function_name] = 0
self.count_dict[function_name] += 1
return func(self, *args, **kwargs)
return wrapper
return decorator
Код: Выделить всё
class A(FunctionCount):
def __init__(self, count_dict: dict[str, int]) -> None:
super().__init__(count_dict)
@FunctionCount.count()
def func(self) -> None:
pass
@FunctionCount.count("custom_name")
def func2(self) -> None:
pass
a.func()
a.func()
a.func2()
print(count_dict)
assert count_dict == {"A.func": 2, "custom_name": 1}
Когда я вызываю метод a.func, я получаю следующую ошибку mypy:
Недопустимый собственный аргумент «A» для атрибута функции «func» с типом «Callable[[Never], None]» mypy(misc)
Думаю, использование декоратора вместо оболочки вызвало эту ошибку, но я не могу понять, почему и что мне следует сделать, чтобы ее исправить.
Кто-нибудь знает, как чтобы таким образом иметь правильно набранный декоратор?
Подробнее здесь: https://stackoverflow.com/questions/788 ... references