When should I store the result of a function as a variable in python?
假设函数my_list(obj)返回一个列表。 我想编写一个函数,如果此列表的长度为1,则返回my_list(obj)的单个元素,否则返回False。 我的代码是
1 2 3 4
| def my_test(obj):
if len(my_list(obj)) == 1:
return my_list(obj)[0]
return False |
它刚刚告诉我代码
1 2 3 4 5
| def my_test(obj):
L = my_list(obj)
if len(L) == 1:
return L[0]
return False |
可能效率更高,因为它只调用my_list()一次。 这是真的?
函数my_list()可能是计算密集型的,所以我很好奇这两个代码块之间是否存在差异。 我很乐意自己做一个测试,但我不太清楚该怎么做。 我也很好奇,如果函数的结果被多次调用,那么将函数的结果存储为变量通常是更好的做法。
-
那是正确的。如果my_list()需要花费大量时间来执行,那么存储结果会更好。
-
沃尔夫的评论是正确的。另外,有关如何自行测试的指导,请参阅以下问题:stackoverflow.com/questions/2866380/…
-
如果my_list有一些副作用或者只是不是幂等的,那也很重要。例如,如果它是一个返回列表的生成器。或者,如果obj可以在调用之间进行更改。
-
几乎在所有情况下,您都不需要担心性能,应该编写任何更易读的代码。只在你需要的时候回去优化。如果my_list是一个昂贵的调用,那么值得将它保存在变量中。这也是一个方便的装饰者:docs.python.org/3/library/functools.html#functools.lru_cache
-
@freebie这个链接对我来说非常有启发性。我一直在研究另一段代码,其中我本质上是试图lru_cache自己(我是一名数学家,为我的研究编写代码并且对计算机科学几乎一无所知)。谢谢您的帮助!
-
@BrianFitzpatrick很高兴它有帮助;我几乎没有包含它。我是一名计算机科学家,对数学几乎一无所知。很高兴见到你。
你是对的。第二个块效率更高,因为它只调用my_list()一次。如果my_list()的计算成本不是特别高,那么您根本不会注意到差异。另一方面,如果你知道它会很昂贵,最好将结果保存在可能的地方,如果它不妨碍可读性(但是,请注意@Tchemate对可能异常的内存的回答)。
但是,如果my_list()有副作用,或者它的返回值可能会在这两个调用之间发生变化,您可能不想保存它(取决于您是否要触发副作用两次或需要接收更改的返回值) 。
如果您想自己测试一下,可以像这样使用time.time:
1 2 3 4 5 6 7
| import time
t0 = time.time()
my_test()
t1 = time.time()
total = t1-t0 |
获得my_test()的时间。只需运行两个函数并比较它们的时间。
-
不要使用time.time进行基准测试,它不是为此设计的,并且可能在所有平台上都不准确。相反,(timeit)[docs.python.org/3.5/library/timeit.html]模块是为此目的而设计的。
回答你的问题,如果一个函数的结果被多次调用,它是否通常更好地存储函数的结果:它取决于。就可读性而言,作为一名程序员,这完全取决于你。就速度而言,将结果存储在变量中通常比运行两次函数更快。
但是,存储结果可能会使用内存,如果存储的是异常大的变量,则内存使用量实际上会导致比简单调用函数更长的运行时间。此外,如上所述,运行函数不仅可以将结果存储在变量中,因此运行不同次数的函数可以给出不同的结果。
-
那个大变量已经在内存中了(因为它是从函数返回的)。添加另一个引用将不会大大增加像python这样的语言的内存占用,因为您只是将相同的内存中对象分配给另一个名称。这不是复制数据的情况。
-
@IanMcLaird不太好。如果将较大的值存储在变量中,然后再次调用该函数(如OP的示例中所示),则使用两倍的内存就像调用函数两次一样(假设函数分配了一个新的块)记忆,这可能发生在这是一个问题的情况下)。
-
OP的第一个示例不会将值存储在本地。 OP的第二个例子没有调用该函数两次。是的,如果您存储该值然后重新计算它,那么您将拥有两个世界中最糟糕的一个。根据垃圾收集算法的行为,调用该函数两次可能会创建两倍的内存使用量。调用函数一次并存储对返回值的引用将永远不会这样做(在python中)。
-
@IanMcLaird抱歉,我匆忙并且错误地回忆了这些例子。我的意思是,如果变量长时间保持在范围内(因为调用函数的结果将立即生成),存储变量(如在OPs第二个示例中)可能会导致一般情况下的高内存使用量在运行函数后释放,否则)。