繁体   English   中英

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

[英]x not in set versus x != each element in the set?

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

发言内容正在发生什么:

x not in {None, False}

提出unhashable type: 'list'错误?

我发现的解决方法是改为:

x != None and x!= False

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

合理

以下是官方文档中的说明:

  1. [Python 3]:类[iterable]

    返回一个新的set或frozenset对象,其元素取自iterable 集合的元素必须是可以 清除的

  2. [Python 3]: hashable

    一个目的是可哈希如果它有一个哈希值其寿命(它需要一个在这期间从不改变__hash __()方法),并且可相对于其他对象(它需要一个__eq __()方法)。 比较相等的可哈希对象必须具有相同的哈希值。
    ...
    所有Python的不可变内置对象都是可清除的; 可变容器(例如列表或词典)不是

  3. [Python 3]:对象。 __contains __self,item (在锚点上方):

    成员资格测试运算符( innot in )通常实现为序列的迭代。 但是, 容器对象可以使用更高效的实现提供以下特殊方法,这也不要求对象是序列。

进入[GitHub]:python / cpython - (v3.5.4)cpython / Objects / setobject.c

  • 线#1991

     static PyMethodDef set_methods[] = { {"add", (PyCFunction)set_add, METH_O, add_doc}, {"clear", (PyCFunction)set_clear, METH_NOARGS, clear_doc}, {"__contains__",(PyCFunction)set_direct_contains, METH_O | METH_COEXIST, // @TODO - cfati: MARK THIS LINE 
  • #1843行

     static PyObject * set_direct_contains(PySetObject *so, PyObject *key) { long result; result = set_contains(so, key); // @TODO - cfati: MARK THIS LINE if (result == -1) return NULL; return PyBool_FromLong(result); } 
  • #1823行

     static int set_contains(PySetObject *so, PyObject *key) { PyObject *tmpkey; int rv; rv = set_contains_key(so, key); // @TODO - cfati: MARK THIS LINE if (rv == -1) { 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); // @TODO - cfati: MARK THIS LINE Py_DECREF(tmpkey); } return rv; } 
  • #627行

     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 } 
  • #614行

     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; } 

从“callstack”(以相反顺序呈现)中可以看出,为了测试成员资格( in / not in ),正在候选成员(“ includee ”)上执行散列 (在所有代码路径上),并且由于list实例没有散列函数,解释器吐出TypeError

解析度

有很多方法可以解决这个问题(正如许多其他人已经指出的那样):

  • 使用不需要其元素的容器( 列表元组
  • 测试__hash__成员
  • try / except块中包含成员资格测试
  • 为元素使用aable容器( 元组 ): x = ('a', 'b')

但(通常)这些只是解决问题的方法(这是我个人的意见),因为如果你最终将列表与NoneFalse进行比较,那么代码(产生该列表)可能会使用一些重构。

如果您可以在set输入要测试的所有元素,则表示所有不可用元素都不属于您的集合(因为您无法将它们放入)

你可以这样做:

if x.__hash__ and x in {None,False}:

当object不可x.__hash__时, x.__hash__None (其他替代方法: 询问“是否可以”关于Python值 )并且第二部分未被评估。

或(更好地请求宽恕而非许可):

def belongs(x):
    try:
        return x in {None,False}
    except TypeError:   # unhashable type
        return False

两种解决方案都比使用listtuple更快( (None,False) ),因为没有涉及线性搜索(即如果测试列表中有很多元素,对于仅2个元素则不正确)

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

相反,您可以使用元组来执行相同类型的成员资格比较。 元组元素不需要是可以清除的。

x not in (None, False)

我想对set vs list成员资格测试做一个简短的比较

成员资格测试调用__contains__ 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

但为什么以上情况不适用于套装? 成员资格测试在列表和集合中以不同的方式工作。 事实列表和集合的实现方式不同。 谈到集合,它们是使用Hash-Table 这允许sets执行成员资格测试,即与查找为O(n) list相比,在O(1)中进行查找。 因此,当in一组进行, __contains__尝试计算hash需要使用加以研究对象的__hash__ 由于列表在python中不可用,因此会出现错误: TypeError: unhashable type: 'list' 如果您对列表执行相同操作,则不会出现任何错误,因为列表不会为成员资格测试计算哈希值。

简而言之,不能对具有不可用对象的集合执行成员资格测试。 一般来说,所有可变对象(list, sets, dict)都是不可删除的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM