2015-04-17 13 views
25

Jeśli zrobimy patologiczny ziemniak tak:Dokonywanie przedmiot x takie, że „x w [x]” zwraca false

>>> class Potato: 
...  def __eq__(self, other): 
...   return False 
...  def __hash__(self): 
...   return random.randint(1, 10000) 
... 
>>> p = Potato() 
>>> p == p 
False 

Możemy przełamać zbiorów i dicts ten sposób (uwaga: to samo nawet jeśli __eq__ powraca True, to mucking z mieszania, które je złamał):

>>> p in {p} 
False 
>>> p in {p: 0} 
False 

także len({p: 0, p: 0}) == 2 i {p: 0}[p] podnosi KeyError, przede wszystkim związanych z odwzorowaniem rzeczy wychodzi przez okno, jak oczekiwać ed.

Ale co ja nie spodziewałem się, że nie mogęlisty przerwa

>>> p in [p] 
True 

Dlaczego tak jest? Wygląda na to, że list.__contains__ iteruje, ale najpierw sprawdza, czy równość jest równa. Ponieważ nie jest tak, że tożsamość oznacza równość (patrz na przykład obiekt NaN), jaki jest powód, dla którego listy zwierają porównania tożsamości?

+0

Może 'list .__ zawiera__' może porównywać obiekty według' id() 'zamiast' eq() '? '(id (p) == id (p)) to prawda' –

+1

@jonrsharpe OP już o tym wie. Myślę, że chce zrozumieć, dlaczego List najpierw sprawdza tożsamość obiektu, a nie równość, jak sądzę. – thefourtheye

+0

@ HåkenLid tak, tak to robi, myślę, że OP pyta * dlaczego *. – jonrsharpe

Odpowiedz

11

list, tuple itp rzeczywiście zrobić sprawdzenie tożsamości przed check równości, a takie zachowanie jest motywowane these invariants:

assert a in [a] 
assert a in (a,) 
assert [a].count(a) == 1 
for a in container: 
    assert a in container # this should ALWAYS be true 

Niestety dict s, set s i przyjaciół działać przez mieszań , więc jeśli zadzierasz z tymi, które faktycznie możesz skutecznie je złamać.

Zobacz niektóre z historii: this issue i this issue.

+1

Nie rozumiem, co masz na myśli mówiąc "to zachowanie jest motywowane przez NaN". Dlaczego zachowanie, które powoduje dziwne zachowanie w NaN, może być motywowane przez NaN? To sprawia, że ​​brzmi to tak, jakby zrobili to w taki sposób, aby złamać NaN. – BrenBarn

8

Ogólnie łamanie założenia, że ​​tożsamość oznacza równość, może przerwać różne rzeczy w Pythonie. Prawdą jest, że NaN łamie to założenie, a zatem NaN przerywa niektóre rzeczy w Pythonie. Dyskusję można znaleźć w this Python bug. W przedpremierowej wersji Pythona 3.0, poleganie na tym założeniu zostało usunięte, ale rozdzielczość błędu polegała na ponownym wstawieniu go (tj. Sprawienie, aby Python 3 działał tak samo jak Python 2, w którym skrót sprawdzania tożsamości jest Gotowe). documentation dla Pythona 3 poprawnie mówi:

Dla typów kontenerów takich jak listy, krotki, zestaw, frozenset, dict lub collections.deque wyrażenie x in y jest równoważna any(x is e or x == e for e in y).

Okazuje się jednak, dokumentacja dla Pythona 2 jest niepoprawny, ponieważ mówi:

list i krotka typów, x na y jest prawdziwe wtedy i tylko wtedy, gdy istnieje indeks i takie że x == y [i] jest prawdziwe.

Możesz zgłosić błąd związany z dokumentacją, jeśli chcesz, chociaż jest to dość ezoteryczny problem, więc wątpię, czy znajdzie się wysoko na czyjejkolwiek liście priorytetów.

+2

Czy ta dokumentacja w 3.x jest nieprawidłowa dla np. 'set', jako dokumenty PO? To sprawdza hashe, a nie tożsamość. – jonrsharpe

+0

@jonrsharpe: To prawda. Skupiałem się na anomalnym zachowaniu listy/tuple case. Używanie takiego losowego skrótu jest jeszcze bardziej patologiczne niż obiekt nie będący sobą. – BrenBarn

+0

Teraz jestem zdezorientowany - korzystałem z Code2Go na iPhone'a, gdzie 'p w {p}' było 'Prawdą' dla 2.7.9 i 3.4.2. Teraz jestem na MacBooku Yosemite, gdzie 'p in {p}' to 'False' dla 2.7.9 i 3.4.3. – jonrsharpe

Powiązane problemy