任务
给定两个字典,需要找到两个字典都包含的键(交集),或者同时属于两个字典的键(并集)。
解决方案
有时,尤其是在Python2.3中,你会发现对字典的使用完全是对集合的一种具体化的体现。在这个要求中,只需要考虑键,不用考虑键的对应值,一般可以通过调用dict.fromkeys 来创建字典,像这样:
a = dict.fromkeys(xrange(1000))
b = dict.fromkeys(xrange(500,1500))
最快计算出并集的方法是:
union = dict(a,**b)
而最快且最简洁地获得交集的方法是:
inter = dict.fromkeys([x for x in a if x in b])
如果字典a和b的条目的数目差异大,那么在for子句中用较短的那个字典,在if子句中用较长的字典会有利于提升运算速度。在这种考虑之下,牺牲简洁性以获取性能似乎是值得的,交集计算可以被改为:
if len(a) < len(b):
inter = dict.fromkeys([x for x in a if x not in b])
else:
inter = dict.fromkeys([x for x in b if x not in a])
Python 也提供了直接代表集合的类型(标准库中的sets模块,在 Python 2.4中已经成为了内建的部分)。可以把下面的代码片段用在模块的开头,这个代码片段确保了名字 set被绑定到了适合的类型,这样在整个模块中,无论你用 Python 2.3 还是 2.4,都可以使用同样的代码:
try:
set
except NameError:
from sets import Set as set
这样做的好处是,可以到处使用 set类型,同时还获得了清晰和简洁,以及速度的提升(在Python2.4中):
a = set(xrange(1000))
b = set(xrange(500,1500))
union = a|b
inter = a&b
讨论
虽然 Python 2.3的标准库模块 sets 已经提供了一个优雅的数据类型 set来代表集合(带有可哈希(hashable)的元素),但由于历史原因,使用 dict 来代表集合仍然是很普遍的。基于这个目的,本节展示了如何用最快的方法来计算这种集合的交集和并集。本节的代码在我的计算机上,并集计算耗时260us,交集计算则耗时690us(Python 2.3;260ms和 600s),而其他的基于循环或者生成器在Python2.4中,这两个数字分别是表达式的方法会更慢。
不过,最好还是用 set类型而不是字典来代表集合。如同本节所示,使用set能让代码更加直接和易读。如果你不喜欢或操作符()和与操作符(&),可以使用等价的a.union(b)和a.intersection(b)。这样操作除了清晰,速度也有提升,特别是在 Pyton 2.4中,计算并集需要 260us,但计算交集只需要 210us。即使是在 Python 2.3,其速度也是可以接受的:并集计算耗时 270us,交集计算耗时650us,没有在Python 2.4快,但如果你仍然用字典来代表集合的话,速度其实是相当的。最后一点,一旦你引入set类型(无论是 Python 2.4 内建的,还是通过 Python 标准库 sets 模块引入的,接口是一样的),你将获得丰富的集合操作。举个例子,属于a或者b但却不属于a和b的交集的集合是a^b,可以等价地被表示为 a.symmetric_difference(b)。
即使由于某些原因使用了 dict,也应当尽可能地用set 来完成集合操作。举个例子,假设你有个字典phones,将人名映射到电话号码,还有个字典address,将人名映射到地址。最清楚简单地打印所有同时知道地址和电话号码的人名及其相关数据的方法是:
for name in set(phones) & set(addresses):
print name,phones[name],addresses[name]
跟下面的方法比,这非常简洁,虽然清晰度可能还有争议:
for name in phones:
if name in addresses:
print name,phones[name],addresses[name]
另一个很好的可选方法是:
for name in set(phones).intersection(addresses):
print name,phones[name],addresses[name]
如果使用 intersection 方法,而不是&交集操作,就不需要将两个字典都转化成 set,只需要其中一个。然后再对转化后的set调用intersection,并传入另一个dict 作为intersection方法的参数。