如果经常使用计算,是否应该将其存储在变量中?

时间:2019-04-05 19:51:41

标签: python performance variables optimization time

例如,如果我有一个功能可以检查list1是否为list2的子列表,哪个选项更好:

选项1:

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:


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中,我使用名为part的变量来存储list2的一部分,但是在选项2中,part不是变量,是{{1}的部分}在需要时进行计算。选项1更快吗?它会花费更多空间吗?

我的问题不是专门针对此功能的,我知道还有其他方法可以实现此功能。

我想知道哪一个是循环中的最佳实践:使用变量来避免多次计算相同的事物。答案是否取决于计算的复杂性和频率?

2 个答案:

答案 0 :(得分:2)

存储本地更好,因为python的查找速度更快。甚至还可以在本地存储功能。

最好通过定时对性能问题进行解答-您可以使用timeit对其进行度量:

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

在这种情况下,全局上下文的max()只需要查找一次,并且在本地进行进一步的查找,基于这些数字,查找速度要快11%。

如果您多次使用本地m(),则可以付费。


在您的情况下,缓存len_list1 = len(list1)是明智的-因为它使用了很多时间并且不会更改。

要使其更具可读性,可以考虑:

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]))

输出:

True
True
False
True

查询:


您的更快版本以及已缓存的长度(基于Scott Mermelstein答案):

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))

传送(两次执行):

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

答案 1 :(得分:2)

作为Patrick出色的answer的补充,让我们尝试使用您的实际代码进行计时:

>>> 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以便更好地反映代码的使用方式,但是至少在我这方面,保存临时变量似乎是个好主意。

顺便说一句,让我们做另一个有趣的实验:

>>> 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.470875242026523 6.463623657007702

6.151073662971612 5.787795798969455

5.685607994964812 5.655005165026523

6.439315696014091 6.372227535001002

请注意,每次缓存的时间比未缓存的时间要少,尽管通过缓存切片并不能获得与性能几乎相同的改善。

还请注意,不要一次运行就得出太多结论,这一点很重要。还有很多其他因素会影响时间,(在我的例子中,很明显,在一次测试中,发生了一些事情使它从6.4下降到5.7!),所以如果您想提出一个好的规则您可以依靠,进行几次测试以确保获得一致的结果。