python有参装饰器如果实现默认参数可调用

问题产生

现在我有一个装饰器,目的是可以延迟执行某个函数。

python 复制代码
def delay(timeout: int = 1):
    def inner_func(func):
        def execute(*args, **kwargs):
            time.sleep(timeout)
            return func(*args, **kwargs)
        return execute
    return inner_func

@delay
def run_task():
    print("finished")

run_task()

看着完美无缺,肉眼看着应该是延迟1s打印finished,但是运行的时候告诉我:

TypeError: delay.<locals>.inner_func() missing 1 required positional argument: 'func'

Holyshit!!!!!

告诉我inner_func第一个参数缺失?我就纳闷的,这个为什么会缺失呢?这个不是待执行的函数吗?

然后我就debug,在def delay第一行打印了一下timeout的值

python 复制代码
def delay(timeout: int = 1):
    print(timeout)

输入结果是<function run_task at 0x10339fe20>

好的人傻了,原来是因为@delay这个写法,如果不加()会把接下来要运行的函数作为参数传入。

可以理解为当写@delay的时候,第一个参数实际上是def run_task

而当写@delay() 或者 delay(1) 或者 delay(timeout=2)的时候,才是按照我们声明的顺序来的

如何解决

我的解决方案是:

python 复制代码
import time
from typing import Callable, Optional


def delay(fn: Optional[Callable] = None, /, *, timeout: int = 1):
    """
    :param fn:      
    :param timeout: 
    :return: 
    """
    def inner_func(func):
        def execute(*args, **kwargs):
            time.sleep(timeout)
            return func(*args, **kwargs)

        return execute

    if callable(fn):
        return inner_func(fn)
    if fn is not None:
        raise Exception("装饰器的参数必须以key-value的形式进行传递")
    return inner_func


@delay
def run_task():
    print("finished")

run_task()

既然有参装饰器的第一个参数是可变的,我干脆不用第一个值了,就直接用fn作为位置参数给他占着。 并且fn后面的参数必须声明为k-v的形式(虽然说写的时候稍微麻烦了一点,但是阅读方便啊)

那么就有两种case:

  1. 如果fn是一个函数,那么说明是用@delay方式声明的,fn肯定就是待执行的方法,那么直接把fn传给inner_func即可
  2. 如果fn不是函数,说明就是正常的装饰器,就按照正常的装饰器声明即可。
相关推荐
蜗牛互联网7 小时前
WSL Containers GA:本地AI容器的生命周期、网络与治理验收
java·网络·人工智能·后端
Zhou1411368 小时前
SpringBoot_02_自动配置原理
java·spring boot·后端
子兮曰9 小时前
Node.js 50个优势场景盘点:一个人单干,为啥我多数时候只用它
前端·后端
IT_陈寒9 小时前
我花3小时debug,就因为JavaScript这个隐式转换坑
前端·人工智能·后端
程序猿阿越10 小时前
containerd如何拉取镜像
后端·kubernetes·源码阅读
一条小小yu10 小时前
为什么使用springboot
java·spring boot·后端
香瓜子rd10 小时前
MySQL InnoDB 并发控制核心原理:事务、隔离级别、MVCC 与锁机制
数据库·后端
RISCV_Explorer10 小时前
RISC-V RVV向量编程模型机制解析——vsetvl配置、掩码与尾元素策略及长度无关编程
后端·risc-v
对象存储与RustFS10 小时前
升级 RustFS 二进制不停机:一条一条换,留一条退路
后端·rust·开源
hasty10 小时前
限制写了却没生效:OpenTelemetry Go 的 Unicode 截断边界
开发语言·后端·golang