<p>您可以使用<a href="http://docs.python.org/2/library/itertools.html#itertools.compress" rel="nofollow noreferrer">^{<cd1>}</a>,它在选择器中生成与true相对应的元素。在</p>
<p>但是,这将需要复制<code>bits</code>并反转一个副本以选择零的元素,最终结果是:</p>
<pre><code>from operator import not_
true_values = list(compress(sequence, bits))
false_values = list(compress(sequence, map(not_, bits)))
</code></pre>
<hr/>
<p>我相信使用一个简单的<code>for</code>循环会更容易和更快,因为它只进行一次迭代:</p>
^{pr2}$
<hr/>
<p>出于好奇,以下是一些包含各种解决方案的微基准测试:</p>
<pre><code>In [12]: import random
In [13]: value = 'a' * 17000
In [14]: selectors = [random.randint(0, 1) for _ in range(17000)]
In [15]: %%timeit
...: true_values = [v for v,b in zip(value, selectors) if b == 1]
...: false_values = [v for v,b in zip(value, selectors) if b == 0]
...:
100 loops, best of 3: 2.56 ms per loop
In [16]: %%timeit
...: true_values = []
...: false_values = []
...: for bit,val in zip(selectors, value):
...: if bit:
...: true_values.append(val)
...: else:
...: false_values.append(val)
...:
1000 loops, best of 3: 1.87 ms per loop
In [17]: %%timeit
...: res = {}
...: for val, bit in zip(value, selectors):
...: res.setdefault(bit, []).append(val)
...: true_values, false_values = res.get(1, []), res.get(0, [])
...:
100 loops, best of 3: 3.73 ms per loop
In [18]: from collections import defaultdict
In [19]: %%timeit
...: res = defaultdict(list)
...: for val, bit in zip(value, selectors):
...: res[bit].append(val)
...: true_values, false_values = res.get(1, []), res.get(0, [])
...:
100 loops, best of 3: 2.05 ms per loop
In [26]: %%timeit # after conversion to numpy arrays
...: true_values = values[selectors == 0]
...: false_values = values[selectors == 1]
...:
1000 loops, best of 3: 344 us per loop
In [31]: %%timeit
...: res = [[], []]
...: for val, bit in zip(value, selectors):
...: res[bit].append(val)
...: true_values, false_values = res
...:
100 loops, best of 3: 2.09 ms per loop
In [34]: from operator import not_
In [35]: %%timeit
...: true_values = list(compress(value, selectors))
...: false_values = list(compress(value, map(not_, selectors)))
...:
1000 loops, best of 3: 1.44 ms per loop
</code></pre>
<p>显然<code>numpy</code>比其他的快得多,<em>假设</em>可以用numpy数组替换python列表。在</p>
<p>似乎<code>itertools.compress</code>是最快的非第三方解决方案,在<code>1.44 ms</code>。第二快的是朴素的<code>for</code>,在<code>1.87</code>处有一个<code>if-else</code>,其他的解决方案比{<cd10>}稍微多一些。在</p>
<p>增加元素的数量我所看到的唯一变化是Jon Clement的<code>defaultdict(list)</code>解和newtower的<code>[[], []]</code>解变得比朴素的<code>for</code>+<code>if-else</code>(就像<code>2%</code>快<code>500000</code>)。<code>compress</code>仍然比其他人快30%,而{<cd4>}仍然比<code>compress</code>快4倍左右。在</p>
<p>这种区别对你来说重要吗?如果不是(并配置文件以检查它是否是瓶颈!)我只考虑使用更具可读性的解决方案,这在很大程度上是主观的,由您决定。在</p>
<hr/>
<p>关于我得到的时间安排的最后一句话:</p>
<p>即使<code>compress</code>和您的双列表理解在列表上迭代两次,一个是最快的非第三方解决方案,另一个是最慢的。
这里您可以看到“python级循环”或“显式循环”与“C级循环”或“隐式循环”之间的区别。在</p>
<p><code>itertools.compress</code>是在<code>C</code>中实现的,这使得它可以迭代而不需要太多的intepreter开销。正如你所看到的,这有很大的不同。在</p>
<p>您可以在<code>numpy</code>解决方案中看到更多这一点,它也执行两次迭代而不是一次迭代。在本例中,循环不仅是“在C级别”,而且还完全避免了调用pythonapi在数组上迭代,因为<code>numpy</code>有自己的C数据类型。在</p>
<p>这几乎是CPython</em>中的一条规则:为了提高性能,尝试用隐式循环替换显式循环,使用内置函数或在C扩展中定义的函数。在</p>
<p>Guido van Rossum很清楚这一点,试着读他的<a href="http://www.python.org/doc/essays/list2str.html" rel="nofollow noreferrer">An Optimization Anecdote</a>。在</p>
<p>你可以在<a href="https://stackoverflow.com/questions/12840451/python-string-comparison-pointing-to-the-result">this</a>SO问题中找到另一个类似的例子(免责声明:接受的答案是我的。我利用了二分搜索和字符串相等(>;C级内置)来获得比纯python线性搜索更快的解决方案。在</p>