在Python中,没有完全支持异构数据结构。例如,这失败了:
set(set(1,2),set(2,3))
处理套装的最佳方法是什么?
答案 0 :(得分:12)
使用frozenset,
>>> set([frozenset([1,2]), frozenset([2,3])])
set([frozenset([1, 2]), frozenset([2, 3])])
要表示一组集合,内部集合必须是 frozenset 对象,因为集合的元素必须可散列(所有Python的不可变的内置对象是 hashable )。 frozenset
不可变,set
可变。
答案 1 :(得分:3)
你不能拥有通常意义上的一组集合,但是如果你可以使用frozenset
个对象那么它可以工作:
>>> set([set([1,2]), set([3,4])])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
这确实有效:
>>> set([frozenset([1,2]), frozenset([3,4])])
set([frozenset([1, 2]), frozenset([3, 4])])
答案 2 :(得分:0)
有充分的理由说明一组不起作用。想象一下以下(伪代码):
a = set(range(5)) # {0, 1, 2, 3, 4}
b = set(range(5, 10)) # {5, 6, 7, 8, 9}
c = set(range(5)) # {0, 1, 2, 3, 4}
# we've now created a set of sets, and then will drop `c` because it's redundant
d = set([a, b, c]) # {{0, 1, 2... }, {5, 6, 7...}}
# now we've changed a value inside the set, suddenly everything changes
a.add(6) # {{0, 1, 2..., 6}, {5, 6, 7...}}
# now we can re-add `c`
d.add(c) # {{0, 1, 2..., 6}, {5, 6, 7...}, {0, 1, 2...}}
除了元素突然消失的奇怪行为,或者如果它们是可变的,行为也不同,还有缺乏基于散列的查找。
set实现与dict实现非常相似,可以找到here。如果集合包含给定键,则这是实现。注意它是如何计算对象的哈希值,找到第一个匹配项,然后从哈希中查找?
static int
set_contains_key(PySetObject *so, PyObject *key)
{
long hash;
setentry *entry;
if (!PyString_CheckExact(key) ||
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return -1;
}
entry = (so->lookup)(so, key, hash);
if (entry == NULL)
return -1;
key = entry->key;
return key != NULL && key != dummy;
}
现在,如果我们在上面的示例中修改a
,我们如何执行查找?唯一的解决方案是逐项查找,这将具有O(n)时间复杂度。
幸运的是,上面显示了一个简单的解决方案:一个不可变的集合。幸运的是,Python甚至还有这个内置的frozenset。
对于冻结集,因为它是不可变的,所以可以计算散列,既可以防止意外行为,也可以恢复我们的O(1)查找。
在这种情况下,我们可以执行以下操作:
a = frozenset(range(5))
b = frozenset(range(5, 10))
c = frozenset(range(5))
d = set([a, b, c])
现在,d
将允许使用单独的容器查找单个项目的成员资格,因为冻结成员是不可变的。