首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >作为类编写的简单装饰器,它计算一个函数被调用了多少次

作为类编写的简单装饰器,它计算一个函数被调用了多少次
EN

Code Review用户
提问于 2019-03-10 12:03:17
回答 1查看 127关注 0票数 2

我能改进它的打字吗?你还会做什么其他的改善或仿生改变吗?

代码语言:javascript
运行
复制
F = TypeVar('F', bound=Callable[..., Any])


# This is mostly so that I practice using a class as a decorator.
class CountCalls:
    """Logs to DEBUG how many times a function gets called, saves the result in a newly created attribute `num_calls`."""
    def __init__(self, func: F) -> None:
        functools.update_wrapper(self, func)
        self.func = func
        self.num_calls: int = 0
        self._logger = logging.getLogger(__name__ + '.' + self.func.__name__)
        self.last_return_value = None

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        self.num_calls += 1
        self._logger.debug(' called %s times', self.num_calls)
        self.last_return_value = self.func(*args, **kwargs)
        return self.last_return_value

下面是装修师的作品:

代码语言:javascript
运行
复制
>>> @CountCalls
... def asdf(var: str):
...     print(var)
...     return len(var)
... 
>>> asdf('Laur')
Laur
4
DEBUG:__main__.asdf: called 1 times
>>> asdf('python 3')
DEBUG:__main__.asdf: called 2 times
python 3
8
>>> asdf(3)
DEBUG:__main__.asdf: called 3 times
3
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "C:/Projects/Python/he/src/he/decorators.py", line 156, in __call__
    self.last_return_value = self.func(*args, **kwargs)
  File "<input>", line 4, in asdf
TypeError: object of type 'int' has no len()
>>> asdf.num_calls
3
EN

回答 1

Code Review用户

发布于 2019-03-10 16:57:35

您可以尝试改进类型的一件事是键入方法本身(虽然我不确定工具对它的支持程度)。另外,引导/尾随空格应该由记录器来决定,而不是使用它的代码。

代码语言:javascript
运行
复制
F = TypeVar('F', bound=Callable[..., Any])


# This is mostly so that I practice using a class as a decorator.
class CountCalls:
    """Logs to DEBUG how many times a function gets called, saves the result in a newly created attribute `num_calls`."""
    def __init__(self, func: F) -> None:
        functools.update_wrapper(self, func)
        self.func = func
        self.num_calls: int = 0
        self._logger = logging.getLogger(__name__ + '.' + self.func.__name__)
        self.last_return_value = None

    __call__: F

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        self.num_calls += 1
        self._logger.debug(f'called %s times', self.num_calls)
        self.last_return_value = self.func(*args, **kwargs)
        return self.last_return_value

至于代码本身,您可以创建一个基于回调的API。

代码语言:javascript
运行
复制
F = TypeVar('F', bound=Callable[..., Any])


# This is mostly so that I practice using a class as a decorator.
class CountCalls:
    """Logs to DEBUG how many times a function gets called, saves the result in a newly created attribute `num_calls`."""
    def __init__(self, func: F, callback: Optional[Callable[[int, Tuple[Any], Dict[str, Any]], Any]] = None) -> None:
        if callback is None:
            logger = logging.getLogger(__name__ + '.' + self.func.__name__)

            def callback(num_calls: int, args: Tuple[Any], kwargs: Dict[str, Any]):
                self._logger.debug(f'called %s times', self.num_calls)

        functools.update_wrapper(self, func)
        self.func = func        
        self.callback = callback
        self.num_calls: int = 0
        self.last_return_value = None

    __call__: F

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        self.num_calls += 1
        self.callback(self.num_calls, args, kwargs)
        self.last_return_value = self.func(*args, **kwargs)
        return self.last_return_value

或者使用回调中跟踪的呼叫数量(为了提高灵活性):

代码语言:javascript
运行
复制
F = TypeVar('F', bound=Callable[..., Any])


# This is mostly so that I practice using a class as a decorator.
class CountCalls:
    """Logs to DEBUG how many times a function gets called, saves the result in a newly created attribute `num_calls`."""
    def __init__(self, func: F, callback: Optional[Callable[[int, Tuple[Any], Dict[str, Any]], Any]] = None) -> None:
        if callback is None:
            logger = logging.getLogger(__name__ + '.' + self.func.__name__)
            num_calls: int = 0

            def callback(args: Tuple[Any], kwargs: Dict[str, Any]):
                nonlocal num_calls  # Not sure if this is necessary or not
                num_calls += 1
                self._logger.debug(f'called %s times', self.num_calls)

        functools.update_wrapper(self, func)
        self.func = func        
        self.callback = callback
        self.last_return_value = None

    __call__: F

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        self.callback(self.num_calls, args, kwargs)
        self.last_return_value = self.func(*args, **kwargs)
        return self.last_return_value

更早的时候,为了在使用关键字时传递关键字参数,需要使用@functools.partial(CountCalls, callback=callback)。现在,可以使用@CountCalls(callback=callback)代替。

代码语言:javascript
运行
复制
F = TypeVar('F', bound=Callable[..., Any])


# This is mostly so that I practice using a class as a decorator.
class CountCalls:
    """Logs to DEBUG how many times a function gets called, saves the result in a newly created attribute `num_calls`."""
    def __init__(self, func: F = None, callback: Optional[Callable[[int, Tuple[Any], Dict[str, Any]], Any]] = None) -> None:
        if callback is None:
            logger = logging.getLogger(__name__ + '.' + self.func.__name__)
            num_calls: int = 0

            def callback(args: Tuple[Any], kwargs: Dict[str, Any]):
                nonlocal num_calls  # Not sure if this is necessary or not
                num_calls += 1
                self._logger.debug(f'called %s times', self.num_calls)

        if func is None:
            return functools.partial(CountCalls, callback=callback)

        functools.update_wrapper(self, func)
        self.func = func        
        self.callback = callback
        self.last_return_value = None

    __call__: F

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        self.callback(self.num_calls, args, kwargs)
        self.last_return_value = self.func(*args, **kwargs)
        return self.last_return_value

(注意:这些代码都没有经过测试。)

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/215135

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档