为什么"any()"运行比使用循环慢?

rsm*_*rsm 11 python performance python-3.x

我一直在一个管理大量单词列表的项目中工作,并通过大量测试来验证这些单词是否有效.有趣的是,每次我使用像itertools模块这样的"更快"的工具时,它们似乎都会变慢.

最后我决定问这个问题,因为我可能做错了.以下代码将尝试测试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))  
Run Code Online (Sandbox Code Playgroud)

之前的代码非常能代表我所做的测试,如果我们看一下结果:

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
Run Code Online (Sandbox Code Playgroud)

我发现使用循环比使用循环快一半有点惊人any().对此有何解释?难道我做错了什么?

(我在GNU-Linux下使用了python3.4)

Kas*_*mvd 4

实际上该any()函数等于以下函数:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
Run Code Online (Sandbox Code Playgroud)

这就像你的第二个函数,但是由于any()它本身返回一个布尔值,所以你不需要检查结果然后返回一个新值,所以性能的差异是因为你实际上使用了冗余返回和if条件,也调用any内部另一个函数。

所以这里的优点any是你不需要用另一个函数包装它,因为它会为你做所有的事情。

另外,正如 @interjay 在评论中提到的那样,我错过的最重要的原因似乎是您传递了一个生成器表达式,它any()不会立即提供结果,并且由于它按需生成结果,所以它做了额外的工作。

基于PEP 0289——生成器表达式

生成器表达式的语义相当于创建一个匿名生成器函数并调用它。例如:

g = (x**2 for x in range(10))
print g.next()
Run Code Online (Sandbox Code Playgroud)

相当于:

def __gen(exp):
    for x in exp:
        yield x**2
g = __gen(iter(range(10)))
print g.next()
Run Code Online (Sandbox Code Playgroud)

因此,正如您所看到的,每次 python 想要访问下一个项目时,它都会调用生成器的iter函数和next方法。最后的结果是,在这种情况下使用它是多余的any()

  • 与循环相比,单个附加“if”的影响可以忽略不计。一个更大的区别是“any”版本使用生成器表达式。 (5认同)
  • 该循环做了很多工作,寻找多个子字符串。额外的函数调用和“if”不太可能使所需时间增加一倍。 (3认同)