Should I store a calculation in a variable if it will be used a lot?
例如,如果我有一个函数来检查list1是否是list2的子列表,哪个选项更好:
选项1:
1 2 3 4 5 6 7 8 9 | def isSublist1(list1,list2): "This fuction checks if list1 is a sublist of list2." for i in range(len(list2)): part=list2[i:] # part is a list with all the elements from i to the end of list2 if len(part)<len(list1): return False if list1==part[:len(list1)]: # if list1 is in the beginning of part return True return False |
或选项2:
1 2 3 4 5 6 7 8 | def isSublist2(list1,list2): "This fuction checks if list1 is a sublist of list." for i in range(len(list2)): if len(list2[i:])<len(list1): return False if list1==list2[i:][:len(list1)]: # if list1 is in the beginning of list2[i:] (part) return True return False |
在选项1中,我使用一个名为
我的问题不在于这个函数的特殊性,我知道还有其他方法可以实现这个函数。
我想知道在一个循环中哪一个是最佳实践:使用一个变量来避免计算相同事物的几倍。答案是否取决于计算的复杂性和频率?
补充帕特里克的优秀答案,让我们用您的实际代码来尝试计时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | >>> def isSublist1(list1,list2): ... for i in range(len(list2)): ... part=list2[i:] ... if len(part)<len(list1): ... return False ... if list1==part[:len(list1)]: ... return True ... return False ... >>> def isSublist2(list1,list2): ... for i in range(len(list2)): ... if len(list2[i:])<len(list1): ... return False ... if list1==list2[i:][:len(list1)]: ... return True ... return False ... >>> list1=list(range(10000)) >>> list2=list(range(4000,4020)) >>> import timeit >>> timeit.timeit('isSublist1(list2,list1)', globals=globals(),number=100) 6.420147094002459 >>> timeit.timeit('isSublist2(list2,list1)', globals=globals(),number=100) 12.455138996010646 |
所以在我的系统中,使用临时变量所需的时间大约是不使用临时变量所需时间的一半。
我不知道您的列表和子列表的性质;您可能希望更改list1和list2是如何使用代码的良好反映,但至少在我看来,保存临时变量是一个非常好的主意。
顺便说一下,让我们做另一个有趣的实验:
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> def isSublist3(list1,list2): ... ln = len(list1) ... for i in range(len(list2)): ... part=list2[i:] ... if len(part)<ln: ... return False ... if list1==part[:ln]: ... return True ... return False ... >>> timeit.timeit('isSublist1(list2,list1)',globals=globals(),number=100); timeit.timeit('isSublist3(list2,list1)',globals=globals(),number=100) 6.549526696035173 6.481004184985068 |
我又跑了几次,看看能得到什么:
6.4708752420265236.463623657007702
6.151073662971612年5.787795798969455号
5.6856079949648125.655005165026523
6.439315696140916.372227535001002
请注意,每次缓存的长度比非缓存的长度花费的时间要少,尽管缓存切片并不能提高性能。
还要注意,不要从一次时间运行中得出太多的结论,这一点很重要。有很多其他的变量影响了时间(在我的例子中,很明显,发生了一些事情,使它从6.4下降到5.7——在一个测试的中间!)所以,如果你想想出一个好的规则,你可以依靠,测试几次,以确保你得到一致的结果。
存储本地更好,因为python的查找速度更快。在本地存储函数甚至是有回报的。
绩效问题最好通过计时来回答-您可以使用时间IT来衡量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import timeit def noTempFunc(): for _ in range(200): max([1,4,5,6]) def tempFunc(): m = max for _ in range(200): m([1,4,5,6]) print(timeit.timeit(noTempFunc, number=1000)) # 0.055301458000030834 print(timeit.timeit(tempFunc, number=1000)) # 0.049811941999905684 : 11% faster |
在这种情况下,只需查找一次全局上下文的
如果你多次使用你当地的
在您的情况下,缓存
为了使其更具可读性,您可以考虑:
1 2 3 4 5 6 7 8 9 10 11 | def isSublist(list1, list2): """Checks if list2 is a sublist of list1""" len_part = len(list2) # reused inside the list comp, only"calulated" once return any( x == list2 for x in (list1[i:i+len_part] for i in range(len(list1)-len_part+1) )) print(isSublist([1,2,3,4],[1])) print(isSublist([1,2,3,4],[2,3])) print(isSublist([1,2,3,4],[1,2,4])) print(isSublist([1,2,3,4],[1,2,3,4])) |
输出:
1 2 3 4 | True True False True |
查找:
- any()函数的工作原理是什么?
- 将python列表拆分为其他"子列表",即较小的列表
您的缓存长度更快的版本(基于Scott Mermelstein的回答):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def isSublist1a(list1,list2): # cached length as well l1 = len(list1) for i in range(len(list2)): part=list2[i:] if len(part)<l1: return False if list1==part[:l1]: return True return False list1=list(range(1000)) list2=list(range(400,420)) import timeit print(timeit.timeit('isSublist1(list2,list1)', globals=globals(),number=1000)) print(timeit.timeit('isSublist1a(list2,list1)', globals=globals(),number=1000)) print(timeit.timeit('isSublist2(list2,list1)', globals=globals(),number=1000)) |
交付(2次执行):
1 2 3 4 5 6 7 | 0.08652938600062043 # cached part 0.08017484299944044 # cached part + cached list1 lenght - slightly faster 0.15090413599955355 # non-cached version 0.8882850420004615 # cached part 0.8294611960000111 # cached part + cached list1 lenght - slightly faster 1.5524438030006422 # non-cached version |