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不是函数,说明就是正常的装饰器,就按照正常的装饰器声明即可。
相关推荐
q***11651 小时前
Spring 中的 @ExceptionHandler 注解详解与应用
java·后端·spring
用户21411832636022 小时前
Gemini 3 Pro 来了!一句话生成完整网站,AI编程能力断层领先
后端
码事漫谈2 小时前
Linux开发到底指什么?是什么岗位?做什么的?
后端
码事漫谈3 小时前
Windows开发:一场与指针的共舞,亦是超越它的征程
后端
f***45323 小时前
基于SpringBoot和PostGIS的各省与地级市空间距离分析
android·前端·后端
Felix_XXXXL4 小时前
mysql查看binlog日志
java·后端
leonardee4 小时前
Plugin ‘mysql_native_password‘ is not loaded`
java·后端
百锦再4 小时前
第17章 模式与匹配
开发语言·后端·python·rust·django·内存·抽象
百***86465 小时前
springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);docker中同时部署应用和libreoffice
spring boot·后端·docker
MZ_ZXD0015 小时前
springboot流浪动物救助平台-计算机毕业设计源码08780
java·spring boot·后端·python·spring·flask·课程设计