【问题标题】:How to have a set of sets in Python? [duplicate]如何在 Python 中拥有一组集合? [复制]
【发布时间】:2016-09-03 11:50:57
【问题描述】:

在 Python 中,不完全支持异构数据结构。例如这会失败:

set(set(1,2),set(2,3))

处理集合的最佳方法是什么?

【问题讨论】:

  • 我认为您的示例失败有两个原因。 #1 因为 set 构造函数只接受一个参数,而 #2 因为 set 是可变的,因此不可散列。您可以通过使用不可变的 freezeset 来修复 #2,通过使用 set 文字或将元素包装在列表中来修复 #1。
  • @SwiftsNamesake 我认为这应该是一个答案。
  • @athan 也许,但有两个人已经打败了我:P

标签: python list python-2.7 set


【解决方案1】:

您不能拥有通常意义上的一组集合,但如果您可以使用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】:

    使用frozenset

    >>> set([frozenset([1,2]), frozenset([2,3])])
    set([frozenset([1, 2]), frozenset([2, 3])])
    

    要表示一组集合,内部集合必须是 frozenset 对象,因为集合的元素必须是 hashable(所有 Python 的 不可变内置对象是可散列)。 frozenset不可变的set可变的

    【讨论】:

      【解决方案3】:

      一组集合不起作用是有充分理由的。想象一下(伪代码):

      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

      使用frozenset,因为它是不可变的,所以可以计算哈希,既可以防止意外行为,又可以恢复我们的 O(1) 查找。

      在这种情况下,我们可以这样做:

      a = frozenset(range(5))
      b = frozenset(range(5, 10))
      c = frozenset(range(5))
      d = set([a, b, c])
      

      现在,d 将允许在单独的容器中查找单个项目的成员资格,因为frozenset 成员是不可变的。

      【讨论】:

      • 此代码应有错误。假新闻
      猜你喜欢
      • 2016-02-27
      • 2018-03-08
      • 1970-01-01
      • 2014-05-23
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      相关资源
      最近更新 更多