Python x不在集合中对x!=集合中的每个元素?

Python x不在集合中对x!=集合中的每个元素?,python,python-3.x,Python,Python 3.x,假设我们有x=['a','b'] 在声明的幕后发生了什么: x not in {None, False} 这会引发不可损坏类型:“list”错误 我发现的解决方法是写以下内容: x != None and x!= False 但我感到困惑,因为从数学上讲,这两个布尔表达式是等价的。{None,False}是一个集合。集合只能包含可哈希对象,因此只能测试可哈希对象的成员资格。列表是不可散列的 x not in (None, False) 相反,您可以使用元组来执行相同类型的成员比较。元组元素

假设我们有
x=['a','b']

在声明的幕后发生了什么:

x not in {None, False}
这会引发
不可损坏类型:“list”
错误

我发现的解决方法是写以下内容:

x != None and x!= False

但我感到困惑,因为从数学上讲,这两个布尔表达式是等价的。

{None,False}
是一个集合。集合只能包含可哈希对象,因此只能测试可哈希对象的成员资格。列表是不可散列的

x not in (None, False)
相反,您可以使用元组来执行相同类型的成员比较。元组元素不需要是可散列的

x not in (None, False)

如果您可以在
集合中输入所有要测试的元素,则意味着所有不可损坏的元素都不属于您的集合(因为您无法将它们放入)

你可以做:

if x.__hash__ and x in {None,False}:
当对象不可散列时,
x.uuu hash\uuuu
None
(其他选项:),第二部分不计算

或者(最好请求原谅而不是允许):

这两种解决方案都比使用
列表
元组
(None,False)
)更快,因为不涉及线性搜索(也就是说,如果测试列表中有很多元素,而不仅仅是两个元素)

以下是官方文件所述:

  • :

    返回一个新的集合或冻结集对象,其元素取自iterable。集合的元素必须是

  • :

    如果一个对象的哈希值在其生存期内从未改变(它需要一个方法),并且可以与其他对象进行比较(它需要一个方法),那么它就是可哈希的。比较相等的可散列对象必须具有相同的散列值。

    Python的所有不可变内置对象都是可散列的可变容器(如列表或词典)不可用

  • (就在锚的上方):

    成员资格测试操作符(和)通常作为一个序列的迭代来实现。但是,容器对象可以为以下特殊方法提供更高效的实现,这也不要求对象是序列

  • 进入:

    • 第#1991行:

    • 第1843行:

    • 第1823行:

    • 第627行:

    • 第614行:

    从“callstack”(以相反的顺序显示)中可以看出,为了测试成员资格(
    /
    中而不是在
    中),正在对候选成员(“includee”)执行哈希(在所有代码路径上),并且由于列表实例没有哈希功能,解释器会抛出TypeError

    决议 有很多方法可以绕过这一点(正如许多其他人已经指出的,其中大部分):

    • 使用不要求其元素可散列的容器(列表、元组)
    • 对散列成员的测试
    • 将成员资格测试包装在try/块中,但块除外
    • 对元素使用哈希容器(元组):
      x=('a','b')

    但是(通常)这些只是解决问题的方法(这是我个人的观点),因为如果你最终将一个列表与“无”和“假”进行比较,代码(生成该列表)可能需要一些重构。

    我想对
    集合的成员资格测试与
    列表的成员资格测试做一个简短的比较

    成员资格测试调用dunder(如果类实现此方法)。所以,如果我们写

    >>> 1 in [1,2] 
    
    这相当于

    >>> list.__contains__([1,2],1)
    >>> True
    
    如果我们这样做:

    >>> [1,2] in [1,2,3]
    >>> False #We get False instead of TypeError here
    
    但为什么上述情况不适用于集合?成员资格测试在列表和集合中以不同的方式工作。事实列表和集合的实现方式不同。说到set,它们是使用。这允许
    设置
    执行成员资格测试,即在
    O(1)
    中查找,而在
    列表
    中查找是
    O(n)
    。因此,当对集合执行
    中的
    时,
    \uuuuuu包含
    尝试使用计算需要查看的对象的
    哈希值。自从
    在python中,列表是不可破坏的,您会得到错误:
    TypeError:unhabable type:'list'
    。若你们对列表做同样的操作,你们将不会得到任何错误,因为列表不会为成员资格测试计算哈希


    简而言之,成员资格测试不能在对象不可损坏的集合上执行。一般来说,所有可变对象
    (列表、集合、目录)
    都是不可损坏的

    出于好奇,你能用语言描述一下你真正想要达到的目标吗?我怀疑这两个选项都不是您真正想要的。
    x
    是不可散列的,因此它不能在您的
    set
    中。您正在尝试查找列表对象是否在两个值的集合中(即集合中的两个值之一是否为列表)。列表不能散列,因此不能作为集合的一部分,因此不可比较。你想让这个操作做什么?set()
    中的
    []引发错误而不是计算为
    False
    ,有什么理由吗?@Patrick Python警告你一些无意义的操作,而不是悄悄地失败。因此,如果
    x
    是不可损坏的,那么它就不在集合中:)@Jean-Françoisfare是的,是的。我认为除了TypeError:…
    会更像pythonic。@deceze是的,我想使用短路。异常强制创建函数。在我提供的链接中还有其他选择(比如测试类型aganst
    collections.Hashable
    ,但我怀疑这会很慢),异常不会强制创建函数;这取决于使用此代码的上下文以及您希望它的详细程度/简洁程度。当
    TypeError
    发生或没有发生时,您可能会中止整个操作,因为
    x
    显然是错误的类型…
    static int
    set_contains_key(PySetObject *so, PyObject *key)
    {
        setentry entry;
        Py_hash_t hash;
    
        if (!PyUnicode_CheckExact(key) ||
            (hash = ((PyASCIIObject *) key)->hash) == -1) {  // @TODO - cfati: MARK THIS LINE
            hash = PyObject_Hash(key);
            if (hash == -1)
                return -1;
        }
        entry.key = key;
        entry.hash = hash;
        return set_contains_entry(so, &entry);  // @TODO - cfati: MARK THIS LINE
    }
    
    static int
    set_contains_entry(PySetObject *so, setentry *entry)
    {
        PyObject *key;
        setentry *lu_entry;
    
        lu_entry = set_lookkey(so, entry->key, entry->hash);  // @TODO - cfati: MARK THIS LINE
        if (lu_entry == NULL)
            return -1;
        key = lu_entry->key;
        return key != NULL && key != dummy;
    }
    
    >>> 1 in [1,2] 
    
    >>> list.__contains__([1,2],1)
    >>> True
    
    >>> [1,2] in [1,2,3]
    >>> False #We get False instead of TypeError here