是的,一个微不足道的改变使它更快:
value_holder = set()
(好吧,您还必须将 更改append
为add
。但仍然很简单。)
使用集合而不是列表意味着每次查找都是 O(1) 而不是 O(N),因此整个操作是 O(N) 而不是 O(N^2)。换句话说,如果您有 10,000 行,那么您将进行 10,000 次哈希查找而不是 50,000,000 次比较。
这个解决方案的一个警告 - 以及所有其他发布的 - 是它要求值是可散列的。如果它们不是可散列的,但它们具有可比性,您仍然可以通过使用排序集(例如,来自blist
库)获得 O(NlogN) 而不是 O(N^2)。如果它们既不可散列也不可排序……好吧,您可能想找到某种方法来生成可散列(或可排序)的东西以用作“第一次检查”,然后只对“第一次检查”匹配进行实际匹配,这将使您达到 O(NM),其中 M 是哈希冲突的平均次数。
您可能想查看标准库文档中的配方unique_everseen
是如何实现的。itertools
请注意,字典实际上没有顺序,因此无法选择“第一个”副本;你会得到一个任意的。在这种情况下,还有另一种方法可以做到这一点:
inverted = {v:k for k, v in d.iteritems()}
reverted = {v:k for k, v in inverted.iteritems()}
(这实际上是一种没有任何处理的decorate-process-undecorate习语的形式。)
但是,您可以通过在阅读时进行过滤来使事情变得更好(更简单、更快、更节省内存和保持顺序),而不是构建dict
然后过滤它。基本上,set
边走边dict
看。例如,而不是这个:
mydict = {}
for line in f:
k, v = line.split(None, 1)
mydict[k] = v
mapp = {}
value_holder = set()
for i in mydict:
if mydict[i] not in value_holder:
mapp[i] = mydict[i]
value_holder.add(mydict[i])
只需这样做:
mapp = {}
value_holder = set()
for line in f:
k, v = line.split(None, 1)
if v not in value_holder:
mapp[k] = v
value_holder.add(v)
事实上,您可能需要考虑编写一个one_to_one_dict
将其包装起来的代码(或搜索 PyPI 模块和 ActiveState 配方以查看是否有人已经为您编写了它),那么您可以编写:
mapp = one_to_one_dict()
for line in f:
k, v = line.split(None, 1)
mapp[k] = v