Python 防止函数在一行中被调用两次

Python 防止函数在一行中被调用两次,python,decorator,Python,Decorator,我正在学习decorators,我有一项任务要求我创建一个decorator,以防止函数连续被调用两次。如果再次调用相同的函数,则应返回None 我似乎无法理解它到底是如何工作的,到目前为止我已经做到了: def dont_run_twice(f): global counter def wrapper(*args, **kwargs): counter += 1 if counter == 2: return None

我正在学习decorators,我有一项任务要求我创建一个decorator,以防止函数连续被调用两次。如果再次调用相同的函数,则应返回None 我似乎无法理解它到底是如何工作的,到目前为止我已经做到了:

def dont_run_twice(f):
    global counter
    def wrapper(*args, **kwargs):
        counter += 1
        if counter == 2:

            return None
        else:
            result = f(*args, **kwargs)
            return result

counter = 0
(我知道这是一个非常糟糕的尝试,但我只是不知道如何跟踪使用特定参数调用的函数,以检查它是否已经在之前) 输出应该类似于:

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

myPrint("Hello")
myPrint("Hello")  #won't do anything (only return None)
myPrint("Hello")  #still does nothing.
myPrint("Goodbye")  #will work
myPrint("Hello")  #will work

似乎这可以帮助你:

import functools


def do_not_run_twice(func):
    prev_call = None

    @functools.wraps(func) # It is good practice to use this decorator for decorators
    def wrapper(*args, **kwargs):
        nonlocal prev_call

        if (args, kwargs) == prev_call:
            return None
        prev_call = args, kwargs
        return func(*args, **kwargs)

    return wrapper
试试这个:

my_print("Hello")
my_print("Hello")  # won't do anything (only return None)
my_print("Hello")  # still does nothing.
my_print("Goodbye")  # will work
my_print("Hello")  # will work.

似乎这可以帮助你:

import functools


def do_not_run_twice(func):
    prev_call = None

    @functools.wraps(func) # It is good practice to use this decorator for decorators
    def wrapper(*args, **kwargs):
        nonlocal prev_call

        if (args, kwargs) == prev_call:
            return None
        prev_call = args, kwargs
        return func(*args, **kwargs)

    return wrapper
试试这个:

my_print("Hello")
my_print("Hello")  # won't do anything (only return None)
my_print("Hello")  # still does nothing.
my_print("Goodbye")  # will work
my_print("Hello")  # will work.

这是一个与Andrey Berenda的解决方案非常相似的解决方案,但其工作原理是为函数对象指定一个属性,而不是使用非局部变量。实际的区别是,函数以前的参数在外部可用,这可能有助于调试

从functools导入包装
def不运行两次(func):
@包装(func)
def包装(*args,**kwargs):
如果(args,kwargs)=包装器。\u prev\u args:
一无所获
包装器。_prev_args=args,kwargs
返回函数(*args,**kwargs)
包装器。_prev_args=None
返回包装器
例如:

>>@不要运行两次
... 定义f(x,y):
...     返回x+y
... 
>>>f(1,2)
3.
>>>f(3,4)
7.
>>>f(3,4)#返回None
>>>f(1,2)
3.
>>>f.(上一页)
((1, 2), {})
请注意,这两种解决方案都有一个轻微的缺陷:如果将它们作为位置参数然后作为关键字参数提供,则可以使用相同的参数值调用(反之亦然):

f(5,6) 11 >>>f(x=5,y=6) 11 作为一种解决方法,您可以使用仅位置(或仅关键字)参数声明包装函数:

#仅限位置,需要Python 3.8+
@不要跑两次
定义f(x,y,/):
返回x+y
#仅关键字
@不要跑两次
定义g(*,x,y):
返回x+y
还要注意,如果前面的参数是可变的,则可能会发生奇怪的事情:

>a=[1,2]
>>>b=[3,4]
>>>f(a,b)
[1, 2, 3, 4]
>>>a[:]=[5,6]
>>>b[:]=[7,8]
>>>f([5,6],[7,8])#返回无

这里的第二个函数调用返回
None
,尽管新参数的值或标识与原始参数不相等;它们等于原始参数的当前值,这些值在用作参数后更改。这可能会导致相当微妙的错误,但不幸的是,没有简单的方法来修复它。

这里有一个与Andrey Berenda的解决方案非常相似的解决方案,但它通过为函数对象指定属性而不是使用非局部变量来工作。实际的区别是,函数以前的参数在外部可用,这可能有助于调试

从functools导入包装
def不运行两次(func):
@包装(func)
def包装(*args,**kwargs):
如果(args,kwargs)=包装器。\u prev\u args:
一无所获
包装器。_prev_args=args,kwargs
返回函数(*args,**kwargs)
包装器。_prev_args=None
返回包装器
例如:

>>@不要运行两次
... 定义f(x,y):
...     返回x+y
... 
>>>f(1,2)
3.
>>>f(3,4)
7.
>>>f(3,4)#返回None
>>>f(1,2)
3.
>>>f.(上一页)
((1, 2), {})
请注意,这两种解决方案都有一个轻微的缺陷:如果将它们作为位置参数然后作为关键字参数提供,则可以使用相同的参数值调用(反之亦然):

f(5,6) 11 >>>f(x=5,y=6) 11 作为一种解决方法,您可以使用仅位置(或仅关键字)参数声明包装函数:

#仅限位置,需要Python 3.8+
@不要跑两次
定义f(x,y,/):
返回x+y
#仅关键字
@不要跑两次
定义g(*,x,y):
返回x+y
还要注意,如果前面的参数是可变的,则可能会发生奇怪的事情:

>a=[1,2]
>>>b=[3,4]
>>>f(a,b)
[1, 2, 3, 4]
>>>a[:]=[5,6]
>>>b[:]=[7,8]
>>>f([5,6],[7,8])#返回无

这里的第二个函数调用返回
None
,尽管新参数的值或标识与原始参数不相等;它们等于原始参数的当前值,这些值在用作参数后更改。这可能会导致相当微妙的错误,但不幸的是,没有简单的方法来修复它。

关于“特定参数”有什么评论?您是否希望将
myPrint('a')
myPrint('b')
视为不同?是的,这正是我想要的。当装饰器应用于多个函数时,使用
global
将在此处给出错误的结果。关于“特定参数”的注释是什么?您是否希望将
myPrint('a')
myPrint('b')
视为不同?是的,这正是我想要的。当装饰器应用于多个函数时,使用
global
将在此处给出错误的结果。实际上,我希望最后一次myu print('Hello')能够工作,该函数不应连续调用两次,而不是两次,您不能使用set,因为您的参数不能散列。使用set尝试my_print([]),它将error@MM1我已经更改了答案。这不支持关键字。最好使用
(args,kwargs)
元组来检查以前的调用参数,而不仅仅是
args
。实际上,我希望最后一次打印(“Hello”)能够正常工作,函数不应该连续调用两次,而不是两次。不,您不能使用set,因为您的args不能散列。使用set尝试my_print([]),它将error@MM1我已经更改了答案。这不支持关键字。最好使用
(args,kwargs)
的元组来检查以前的调用参数,而不仅仅是
args