我想循环一个大的二维列表:
authors = [["Bob", "Lisa"], ["Alice", "Bob"], ["Molly", "Jim"], ... ]
并获取一个列表,其中包含作者中出现的所有名称。
当我遍历列表时,我需要一个容器来存储我已经看过的名字,我想知道我是否应该使用列表或字典:
列表:
seen = []
for author_list in authors:
for author in author_list:
if not author in seen:
seen.append(author)
result = seen
用词典:
seen = {}
for author_list in authors:
for author in author_list:
if not author in seen:
seen[author] = True
result = seen.keys()
哪一个更快?还是有更好的解决方案?
答案 0 :(得分:8)
你真的想要set
。集合比列表更快,因为它们只能包含唯一元素,这允许它们实现为哈希表。哈希表允许在if element in my_set
时间内进行成员资格测试(O(1)
)。这与列表形成对比,其中检查元素是否在列表中的唯一方法是依次检查列表中的每个元素(在O(n)
时间内。)
dict
类似于set
,因为它们都只允许使用唯一键,并且两者都实现为哈希表。他们都允许O(1)
成员资格测试。不同之处在于set
只有键,而dict
有键和值(这是此应用程序中不需要的额外开销。)
使用set
,并用itertools.chain()
替换嵌套的for循环,将2D列表展平为1D列表:
import itertools
seen = set()
for author in itertools.chain(*authors):
seen.add(author)
或更短:
import itertools
seen = set( itertools.chain(*authors) )
编辑(感谢,@ jamylak)大型列表的内存效率更高:
import itertools
seen = set( itertools.chain.from_iterable(authors) )
列表清单上的示例:
>>> a = [[1,2],[1,2],[1,2],[3,4]]
>>> set ( itertools.chain(*a) )
set([1, 2, 3, 4])
P.S。 :如果您想要计算您看到每位作者的次数,而不是找到所有唯一作者,请使用collections.Counter
,这是一种针对事物进行优化的特殊字典。< / p>
以下是计算字符串中字符的示例:
>>> a = "DEADBEEF CAFEBABE"
>>> import collections
>>> collections.Counter(a)
Counter({'E': 5, 'A': 3, 'B': 3, 'D': 2, 'F': 2, ' ': 1, 'C': 1})
答案 1 :(得分:3)
set
应该更快。
>>> authors = [["Bob", "Lisa"], ["Alice", "Bob"], ["Molly", "Jim"]]
>>> from itertools import chain
>>> set(chain(*authors))
set(['Lisa', 'Bob', 'Jim', 'Molly', 'Alice'])
答案 2 :(得分:3)
使用dict
或set
way faster然后使用list
import itertools
result = set(itertools.chain.from_iterable(authors))
答案 3 :(得分:2)
您可以使用set -
from sets import Set
seen = Set()
for author_list in authors:
for author in author_list:
seen.add(author)
result = seen
这样你就可以逃避“if”检查,因此解决方案会更快。
答案 4 :(得分:1)
如果您关心查找的效果,列表中的查找是 O(n),而词典中的查找则分摊为 O(1)。
您可以找到更多信息here。
答案 5 :(得分:1)
列表只是按特定顺序存储一堆项目。把你的作者名单想象成一长串的鸽笼,作者的名字在盒子里的纸上写着。这些名字保留在你输入的顺序中,你可以很容易地在任何特定的文件夹中找到作者,但是如果你想知道一个特定的作者是否在任何一个文件夹中,那么你必须仔细查看每个文章,直到找到你追求的名字。您也可以在任意数量的鸽笼中使用相同的名称。
字典有点像电话簿。鉴于作者的姓名,您可以非常快速地检查作者是否列在电话簿中,并找到随其列出的电话号码。但是你只能包括每个作者一次(只有一个电话号码),你不能按照你喜欢的顺序把作者放在那里,他们必须按照对电话簿有意义的顺序。在真正的电话簿中,该顺序是按字母顺序排列的;在Python词典中,顺序是完全不可预测的(当你向字典中添加或删除内容时它会发生变化),但Python可以在字典中找到比在电话簿中更快的条目。
另一方面,套装就像 列出名称而不是电话号码的电话簿。您仍然不能包含多次相同的名称,它可以在集合中也可以不在。并且您仍然无法使用集合中名称的顺序来执行任何有用的操作。但是您可以非常快速地检查名称是否在集合中。
根据您的用例,一组似乎是明显的选择。你不关心你看过作者的顺序,或者你看过每个作者的次数,只是你可以快速检查你以前是否见过某个特定的作者。
列表对这种情况不利;他们会按照您指定的顺序记住重复项,并且搜索速度很慢。但是你也没有必要将键映射到值,这是字典的作用。回到电话簿类比,你没有任何相当于“电话号码”的东西;在您的字典示例中,您所做的相当于编写一个电话簿,其中每个人的号码都列为True
,那么为什么还要列出电话号码呢?
一套OTOH完全符合你的需要。