oro*_*aki 9 python list-comprehension
# I have 3 lists:
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
L2 = [4, 7, 8]
L3 = [5, 2, 9]
# I want to create another that is L1 minus L2's memebers and L3's memebers, so:
L4 = (L1 - L2) - L3 # Of course this isn't going to work
Run Code Online (Sandbox Code Playgroud)
我想知道,做到这一点的"正确"方法是什么.我可以用很多不同的方式来做,但Python的风格指南说应该只有一种正确的方法来做每件事.我从来不知道这是什么.
Bra*_*des 10
以下是一些尝试:
L4 = [ n for n in L1 if (n not in L2) and (n not in L3) ] # parens for clarity
tmpset = set( L2 + L3 )
L4 = [ n for n in L1 if n not in tmpset ]
Run Code Online (Sandbox Code Playgroud)
现在我有一点时间思考,我意识到这L2 + L3件事会创建一个临时列表,立即被抛弃.所以更好的方法是:
tmpset = set(L2)
tmpset.update(L3)
L4 = [ n for n in L1 if n not in tmpset ]
Run Code Online (Sandbox Code Playgroud)
更新:我看到一些关于性能的奢侈声明,我想声称我的解决方案已经尽可能快.创建中间结果,无论它们是中间列表还是必须被重复调用的中间迭代器,总是会比简单地给出L2和L3直接迭代一样慢,就像我在这里做的那样.
$ python -m timeit \
-s 'L1=range(300);L2=range(30,70,2);L3=range(120,220,2)' \
'ts = set(L2); ts.update(L3); L4 = [ n for n in L1 if n not in ts ]'
10000 loops, best of 3: 39.7 usec per loop
Run Code Online (Sandbox Code Playgroud)
所有其他选择(我能想到)都必然比这慢.例如,自己做循环,而不是让set()构造函数执行它们,增加了费用:
$ python -m timeit \
-s 'L1=range(300);L2=range(30,70,2);L3=range(120,220,2)' \
'unwanted = frozenset(item for lst in (L2, L3) for item in lst); L4 = [ n for n in L1 if n not in unwanted ]'
10000 loops, best of 3: 46.4 usec per loop
Run Code Online (Sandbox Code Playgroud)
使用迭代器,它们涉及的所有状态保存和回调显然会更加昂贵:
$ python -m timeit \
-s 'L1=range(300);L2=range(30,70,2);L3=range(120,220,2);from itertools import ifilterfalse, chain' \
'L4 = list(ifilterfalse(frozenset(chain(L2, L3)).__contains__, L1))'
10000 loops, best of 3: 47.1 usec per loop
Run Code Online (Sandbox Code Playgroud)
所以我相信我昨晚给出的答案仍然是遥远的(对于"遥远"的值大于5微秒,显然)是最好的,除非提问者有重复L1并希望每次删除一次副本出现在其他列表中.
update ::: post包含对与frozensets相比较低的集合性能的错误指控的引用.我认为在这个实例中使用冻结集仍然是明智的,即使不需要对集合本身进行散列,只是因为它在语义上更正确.虽然,在实践中,我可能不会打扰额外的6个字符.我没有动力去编辑帖子,所以请注意"指控"链接链接到一些错误运行的测试.评论中记录了血淋淋的细节.:::更新
Brandon Craig Rhodes 发布的第二大块代码相当不错,但由于他没有回应我关于使用冷冻装置的建议(好吧,不是我开始写这篇文章的时候,无论如何),我将继续发布我自己.
手头工作的整个基础是检查一系列值(L1)中的每一个是否属于另一组值; 该组值是内容L2和L3.在这句话中使用"set"这个词就说明了:即使L2并且L3是lists,我们并不真正关心它们的类似列表的属性,例如它们的值所在的顺序或它们包含的每个属性的数量.我们只关心他们共同拥有的价值集(在那里).
如果将该组值存储为列表,则必须逐个检查列表元素,并检查每个元素.这是相对耗时的,而且它的语义很糟糕:再次,它是一组"值",而不是列表.因此Python具有这些整齐的集合类型,它们包含许多独特的值,并且可以快速告诉您是否存在某些值.这与python的dict类型在查找键时的工作方式非常相似.
集合和frozensets之间的区别在于集合是可变的,这意味着它们可以在创建后进行修改.这两种类型的文档都在这里.
由于我们需要创建的集合,存储在L2和中的值的并集L3一旦创建就不会被修改,它在语义上适合使用不可变数据类型.据称这也有一些性能上的好处.嗯,它有一些优势是有意义的; 否则,为什么Python会frozenset作为内置?
更新 ......
布兰登回答了这个问题:冻结套装的真正优势在于它们的不变性使它们可以清洗,允许它们成为字典键或其他套装的成员.
我运行了一些非正式的时序测试,比较了相对较大(3000元素)的冻结和可变集合的创建和查找速度; 差别不大.这与上述链接相冲突,但支持Brandon所说的关于它们相同但在可变性方面的内容.
...... 更新
现在,因为frozensets是不可变的,所以它们没有更新方法.Brandon使用该set.update方法避免创建然后丢弃临时列表以设置创建; 我将采取不同的方法.
items = (item for lst in (L2, L3) for item in lst)
Run Code Online (Sandbox Code Playgroud)
这个生成器表达式使items迭代器连续地遍历L2和的内容L3.不仅如此,它还没有创建一个完整的列表 - 中间对象.for在生成器中使用嵌套表达式有点令人困惑,但我设法通过记住它们按照与编写实际for循环时相同的顺序进行整理,例如,
def get_items(lists):
for lst in lists:
for item in lst:
yield item
Run Code Online (Sandbox Code Playgroud)
该生成器函数等效于我们分配给的生成器表达式items.好吧,除了它是一个参数化的函数定义,而不是直接赋值给变量.
无论如何,足够的题外话.发电机的重要性在于它们实际上并没有做任何事情.好吧,至少不是马上:他们只是设置工作,以便在迭代生成器表达式后再完成.这被正式称为懒惰.我们将通过传递items给frozenset函数来做到这一点(好吧,无论如何),迭代它并返回一个冷冻冷冻集.
unwanted = frozenset(items)
Run Code Online (Sandbox Code Playgroud)
实际上,您可以通过将生成器表达式放在调用内部来实际组合最后两行frozenset:
unwanted = frozenset(item for lst in (L2, L3) for item in lst)
Run Code Online (Sandbox Code Playgroud)
只要生成器表达式创建的迭代器是您正在调用的函数的唯一参数,这种简洁的语法技巧就可以工作.否则你必须在通常单独的括号中写它,就像你将一个元组作为参数传递给函数一样.
现在我们可以像Brandon一样建立一个新列表,并具有列表理解能力.它们使用与生成器表达式相同的语法,并且基本上做同样的事情,除了它们渴望而不是懒惰(再次,这些是实际的技术术语),因此他们可以正确地迭代项目并从中创建列表.
L4 = [item for item in L1 if item not in unwanted]
Run Code Online (Sandbox Code Playgroud)
这相当于将生成器表达式传递给list,例如
L4 = list(item for item in L1 if item not in unwanted)
Run Code Online (Sandbox Code Playgroud)
但更惯用.
因此,这将创建列表L4,其中包含的元素L1不在其中,L2或者L3维护它们最初的顺序以及它们的数量.
如果您只是想知道哪些值在L1但不在L2或中L3,则更容易:您只需创建该集合:
L1_unique_values = set(L1) - unwanted
Run Code Online (Sandbox Code Playgroud)
你可以用它来制作一个列表,就像st0le一样,但这可能不是你想要的.如果你真的想要只找到一组值L1,你可能有一个很好的理由将该集保持为a set或者确实是frozenset:
L1_unique_values = frozenset(L1) - unwanted
Run Code Online (Sandbox Code Playgroud)
...... Annnnd,现在完全不同了:
from itertools import ifilterfalse, chain
L4 = list(ifilterfalse(frozenset(chain(L2, L3)).__contains__, L1))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2953 次 |
| 最近记录: |