代码之家  ›  专栏  ›  技术社区  ›  Tim Robinson

设置非冻结集的成员身份

  •  4
  • Tim Robinson  · 技术社区  · 6 年前

    我想我理解 set frozenset 在python中,但是在set成员中发生了一些神奇的事情( set1 in set2 )我不知道它是怎么工作的。

    设置 属于 弗洛伦塞特 作品:

    >>> s = set()
    >>> s.add(frozenset(['hello', 'world']))
    >>> frozenset(['hello', 'world']) in s
    True
    

    我不能添加不可显示的类型,例如 list 对我 设置 我不能用 in 具有不可显示类型的运算符:

    >>> s.add(['hello', 'world'])
    TypeError: unhashable type: 'list'
    >>> ['hello', 'world'] in s
    TypeError: unhashable type: 'list'
    

    同样,我不能添加 设置 我的集合:

    >>> s.add({'hello', 'world'})
    TypeError: unhashable type: 'set'
    

    …但我可以用 在里面 用一个 设置 检查相应的 弗洛伦塞特 成员:

    >>> {'hello', 'world'} in s
    True
    

    …它给出了正确的结果:

    >>> {'jello', 'world'} in s
    False
    

    为什么是 SET2中的SET1 特殊的?在测试成员身份之前,它真的在计算我的集合的散列吗?或者是因为暴力?

    编辑: 找到了,C实现了 set.__contains__ 知道如何打电话 key = frozenset(key) 无论何时 hash(key) 投掷 TypeError isinstance(key, set) .

    https://github.com/python/cpython/blob/6c7d67c/Objects/setobject.c#L1890-L1897

    rv = set_contains_key(so, key);
    if (rv < 0) {
        if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
            return -1;
        PyErr_Clear();
        tmpkey = make_new_set(&PyFrozenSet_Type, key);
        if (tmpkey == NULL)
            return -1;
        rv = set_contains_key(so, tmpkey);
    
    1 回复  |  直到 6 年前
        1
  •  4
  •   javidcf    6 年前

    查看源代码,当前实现(3.7) set_contains 显示在检查集合中的成员身份时确实将集合对象转换为冻结集合:

    static int
    set_contains(PySetObject *so, PyObject *key)
    {
        PyObject *tmpkey;
        int rv;
    
        rv = set_contains_key(so, key);
        if (rv < 0) {
            if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
                return -1;
            PyErr_Clear();
            tmpkey = make_new_set(&PyFrozenSet_Type, key);
            if (tmpkey == NULL)
                return -1;
            rv = set_contains_key(so, tmpkey);
            Py_DECREF(tmpkey);
        }
        return rv;
    }
    

    基本上,如果给定的对象是一个集合( PySet_Check(key) ,然后创建新的冻结集( make_new_set(&PyFrozenSet_Type, key) )再次检查会员资格( set_contains_key(so, tmpkey) )我不认为这实际上是在任何地方记录的,而且它可能是一个“只起作用”的特性,而您甚至没有注意到如果您不认真考虑它。

    它似乎和被拒绝的人有着同样的精神 PEP-0351 ,尽管它不是基于任何特殊方法的,并且仅限于集。

    编辑:更详细的信息,这个功能显然是在2003年首次实现的。( commit 通过 Raymond Hettinger Alex Martelli . 所以如果他们中的一个恰好在附近,也许他们可以提供更多的背景。

    编辑2:值得注意的是,在特定情况下,这可能会对性能产生重大影响:

    s = set(range(100000))
    sf = frozenset(s)
    t = { sf }
    %timeit sf in t  # True
    >>> 31.6 ns ± 1.04 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
    %timeit s in t  # True
    >>> 4.9 ms ± 168 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    测试是 在第二种情况下,数量级变慢!

    编辑3:I raised an issue 对于python团队来说,讨论取消这种行为的可能性,因为它看起来不一致并且可能有问题。开发人员认为它仍然是一个有用的特性,缺点不值得破坏兼容性。