我一直在一个项目中工作,该项目管理大量的单词列表,并通过大量测试使它们通过,以验证列表中的每个单词是否正确。有趣的是,每当我使用“更快”的工具(如itertools模块)时,它们似乎就会变慢。
itertools
最后,我决定问这个问题,因为我可能做错了什么。以下代码将尝试测试any()函数的性能与使用循环的关系。
any()
#!/usr/bin/python3 # import time from unicodedata import normalize file_path='./tests' start=time.time() with open(file_path, encoding='utf-8', mode='rt') as f: tests_list=f.read() print('File reading done in {} seconds'.format(time.time() - start)) start=time.time() tests_list=[line.strip() for line in normalize('NFC',tests_list).splitlines()] print('String formalization, and list strip done in {} seconds'.format(time.time()-start)) print('{} strings'.format(len(tests_list))) unallowed_combinations=['ab','ac','ad','ae','af','ag','ah','ai','af','ax', 'ae','rt','rz','bt','du','iz','ip','uy','io','ik', 'il','iw','ww','wp'] def combination_is_valid(string): if any(combination in string for combination in unallowed_combinations): return False return True def combination_is_valid2(string): for combination in unallowed_combinations: if combination in string: return False return True print('Testing the performance of any()') start=time.time() for string in tests_list: combination_is_valid(string) print('combination_is_valid ended in {} seconds'.format(time.time()-start)) start=time.time() for string in tests_list: combination_is_valid2(string) print('combination_is_valid2 ended in {} seconds'.format(time.time()-start))
前面的代码很能代表我所做的测试,如果我们看一下结果,则可以:
File reading done in 0.22988605499267578 seconds String formalization, and list strip done in 6.803032875061035 seconds 38709922 strings Testing the performance of any() combination_is_valid ended in 80.74802565574646 seconds combination_is_valid2 ended in 41.69514226913452 seconds File reading done in 0.24268722534179688 seconds String formalization, and list strip done in 6.720442771911621 seconds 38709922 strings Testing the performance of any() combination_is_valid ended in 79.05265760421753 seconds combination_is_valid2 ended in 42.24800777435303 seconds
我发现使用循环比使用快一半,这有点令人惊讶any()。对此有什么解释?难道我做错了什么?
(我在GNU-Linux下使用python3.4)
实际上该any()功能等于以下功能:
def any(iterable): for element in iterable: if element: return True return False
这就像您的第二个函数,但是由于any()返回本身是布尔值,因此您无需检查结果然后返回新值,所以性能的差异是因为您实际上使用了冗余返回和if条件,还调用了any内部另一个函数。
if
any
因此,any这里的优点是您不需要用另一个函数包装它,因为它可以为您完成所有事情。
就像@interjay在评论中提到的那样,似乎我最想念的最重要的原因是您将生成器表达式传递给了any()它,它不会立即提供结果,并且由于它按需产生结果,因此它会做额外的工作。
基于PEP 0289-生成器表达式
生成器表达式的语义等同于创建匿名生成器函数并对其进行调用。例如:
g = (x**2 for x in range(10)) print g.next()
等效于:
def __gen(exp): for x in exp: yield x**2 g = __gen(iter(range(10))) print g.next()
因此,如您所见,每次python要访问下一个项目时,它都会调用生成器的iter函数和next方法,最后结果是any()在这种情况下使用它是过分的。
iter
next