簡體   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