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