You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

学习装饰器:如何实现阻止函数连续两次以相同参数调用的装饰器?

解决装饰器防止函数连续重复参数调用的问题

我明白你的困惑了——你现在的代码只靠计数器判断调用次数,完全没处理「相同参数」这个核心要求,而且全局计数器还会在多个装饰函数间互相干扰,确实没法实现需求。咱们一步步来搞定这个问题:

核心思路

要实现这个装饰器,关键是为每个被装饰的函数单独记录上一次调用的完整参数,然后每次调用时对比当前参数和上一次是否完全一致:

  • 不能用全局变量,得用闭包或者函数属性来保存每个函数的调用状态,避免不同函数互相影响
  • 要把可变的*args和**kwargs转换成可哈希的结构(比如元组),这样才能比较是否相同

完整实现代码

def dont_run_twice(f):
    # 闭包变量:保存上一次调用的参数组合,初始为None表示还未调用过
    last_call_key = None
    
    def wrapper(*args, **kwargs):
        nonlocal last_call_key
        # 把位置参数和关键字参数转换成可哈希的结构
        # args本身是元组,直接用;kwargs转成排序后的键值对元组,保证参数顺序不影响判断
        current_args = args
        current_kwargs = tuple(sorted(kwargs.items()))
        current_call_key = (current_args, current_kwargs)
        
        # 对比当前参数和上一次是否一致
        if current_call_key == last_call_key:
            return None
        
        # 参数不同,正常执行函数并更新记录的参数
        result = f(*args, **kwargs)
        last_call_key = current_call_key
        return result
    
    return wrapper

代码关键点解释

  • nonlocal last_call_key:告诉Python我们要修改闭包中的变量(而不是创建一个新的局部变量),这是Python 3+的语法,如果是Python 2可以改用函数属性(比如f.last_call_key)
  • 处理kwargs的方式:把字典转成排序后的键值对元组,是为了让myPrint(a=1, b=2)和myPrint(b=2, a=1)被视为相同参数,避免因为关键字参数顺序不同误判
  • current_call_key:把位置参数和关键字参数组合成一个元组,作为判断参数是否重复的唯一标识

测试你的示例需求

@dont_run_twice
def myPrint(*args):
    print(*args)

myPrint("Hello")          # 正常打印Hello,记录当前参数
myPrint("Hello")          # 和上一次参数相同,返回None,不执行打印
myPrint("Hello")          # 仍然和上一次参数相同,返回None
myPrint("Goodbye")        # 参数不同,打印Goodbye,更新记录的参数
myPrint("Hello")          # 和上一次(Goodbye)参数不同,正常打印Hello

你的原始代码问题分析

  • 全局counter:会被所有被装饰的函数共享,比如如果有两个函数用这个装饰器,计数器会混乱,完全没法区分不同函数的调用情况
  • 没有处理参数判断:不管参数是什么,只要连续调用两次就返回None,完全不符合「相同参数重复调用才返回None」的需求

内容的提问来源于stack exchange,提问作者MM1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 09:42:41