在保持变量值的同时多次调用函数

时间:2017-05-24 04:48:09

标签: python function class

我的代码的目标是尝试让我的函数只在第一次调用时运行,然后返回第一次调用的答案。然而,看起来“回答”并不是第二次通话的附加版本。我该怎么解决这个问题?

class Attempt:
    def __init__(self, stop):
        self.stop = stop

answer = Attempt([])

def oncefunc(func):
    if answer.stop == []:
        answer.stop.append(func)
        return answer.stop[0]
    else:
        return answer.stop[0]

以下是我将要运行的示例:

def mult(a1, a2):
    return a1 * a2
multonce = oncefunc(mult)

例如,我想调用multOnce(1,2)然后调用multOnce(3,4),但是对于这两个调用我想返回2。

4 个答案:

答案 0 :(得分:1)

你想做的事情叫做“memoization”。首先,在你的代码中,函数永远不会被调用,你只是存储函数对象本身。为了解决这个问题,你需要在oncefunc()中创建一个包装函数并返回它:

(这里我不考虑func的关键字参数以简化)

class Attempt:
    def __init__(self, stop):
        self.stop = stop

answer = Attempt([])

def oncefunc(func):
    def wrapper(*args):
        if answer.stop == []:
            answer.stop.append(func(*args))
            return answer.stop[0]
        else:
            return answer.stop[0]
    return wrapper

def mult(a1, a2):
    print("calculating")
    return a1 * a2

multonce = oncefunc(mult)

print(multonce(1, 2))
print(multonce(1, 2))

然后我们遇到了下一个问题:答案存储在func任何参数的相同位置!因此,如果您第二次使用不同的参数调用multonce,它将返回相同的值。这可以通过使用键作为参数元组的字典来修复:

class Attempt:
    def __init__(self):
        self.answers = {}

answer = Attempt()

def oncefunc(func):
    def wrapper(*args):
        if args not in answer.answers:
            answer.answers[args] = func(*args)
            return answer.answers[args]
        else:
            return answer.answers[args]
    return wrapper

def mult(a1, a2):
    print("calculating")
    return a1 * a2

multonce = oncefunc(mult)

print(multonce(1, 2))
print(multonce(1, 2))
print(multonce(3, 4))

最后一点是将答案数据库保留在外面会使oncefunc中包含的所有函数都是全局的。将它保存在闭包中要方便得多,因此它对oncefunc的每个应用都是唯一的:

def oncefunc(func):
    answers = {}
    def wrapper(*args):
        if args not in answers:
            answers[args] = func(*args)
            return answers[args]
        else:
            return answers[args]
    return wrapper

def mult(a1, a2):
    print("calculating")
    return a1 * a2

multonce = oncefunc(mult)

print(multonce(1, 2))
print(multonce(1, 2))
print(multonce(3, 4))

当然,这是一种非常常见的模式,因此Python已经实现了它:lru_cache

from functools import lru_cache

@lru_cache(maxsize=None)
def mult(a1, a2):
    print("calculating")
    return a1 * a2

print(mult(1, 2))
print(mult(1, 2))
print(mult(3, 4))

答案 1 :(得分:0)

我认为,在你的情况下,class属性会更加可靠,因为你需要在线下的所有调用中保持相同的列表。

由于stop = []是在班级定义的,因此每次调用都会使用与stop列表相同的引用,如下所示。

class Attempt:
    stop = []
    # def __init__(self, stop):
    #     self.stop = stop

answer = Attempt()

def oncefunc(func):

    if answer.stop == []:
        print('entered first')
        answer.stop.append(func)
        return answer.stop[0]
    else:
        print('entered second')
        answer.stop.append(func)
        return answer.stop



def mult(a1, a2):
    return a1 * a2

multonce = oncefunc(mult)
multonce = oncefunc(mult)

print(multonce)

输出:

entered first
entered second
[<function mult at 0x7fc4e3a91cf8>, <function mult at 0x7fc4e3a91cf8>]

这回答你的问题吗?

答案 2 :(得分:0)

您将方法作为参数传递到方法oncefunc,您没有为方法mult提供任何值,您的answer.stop[0]是方法多object,您可以为它提供2个参数,例如:

class Attempt:
    def __init__(self, stop):
        self.stop = stop

answer = Attempt([])

def oncefunc(func):
    if answer.stop == []:
        answer.stop.append(func)
        return answer.stop[0](3,4)
    else:
        return answer.stop[0](3,4)

def mult(a1, a2):
    return a1 * a2

print(oncefunc(mult))  # this print out 12

或者,保持oncefunc不被修改,当您使用2个数值调用mult时:

oncefunc(mult(3,4))

答案 3 :(得分:0)

为什么不使用闭包而不是类?

#This is the outer function, that keeps track of whether its been run or not
def outer(f):
    #You can use a list or boolean
    has_been_run = []
    #wrapper function
    def wrapper(*args, **kwargs):
        if not(has_been_run):
            print "I haven't been run before!"
            #Store the results of the function call
            has_been_run.append(f(*args, **kwargs))
        else:
            #This has been run before, so just return the list
            print "I have been run before!"
            return has_been_run
        #Return the original function
        return f(*args, **kwargs)    
    return wrapper


@outer
def m(a, b):
    return a + b