2015-04-17 28 views
25

Nếu chúng ta thực hiện một củ khoai tây bệnh lý như thế này:Làm một đối tượng x sao cho "x trong [x]" trả về False

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

Chúng tôi có thể phá vỡ bộ và dicts theo cách này (lưu ý: nó giống thậm chí nếu __eq__ lợi nhuận True, nó mucking với băm mà bẻ ra):

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

Cũng len({p: 0, p: 0}) == 2{p: 0}[p] tăng KeyError, về cơ bản tất cả những thứ liên quan lập bản đồ đi ra ngoài cửa sổ, như mong đợi ed.

Nhưng những gì tôi không mong đợi là chúng ta không thể danh sách nghỉ

>>> p in [p] 
True 

Tại sao vậy? Có vẻ như list.__contains__ lặp lại, nhưng trước tiên là checking identity trước khi kiểm tra sự bình đẳng. Vì nó không phải là trường hợp mà danh tính ngụ ý bình đẳng (xem ví dụ đối tượng NaN), lý do cho danh sách ngắn mạch trên so sánh nhận dạng là gì?

+0

Có lẽ 'danh sách .__ contains__' so sánh các đối tượng bằng' id() 'thay vì' eq() '? '(id (p) == id (p)) là True' –

+1

@jonrsharpe OP đã biết về nó. Tôi nghĩ anh ấy muốn hiểu tại sao Danh sách kiểm tra danh tính đối tượng đầu tiên thay vì bình đẳng, tôi đoán vậy. – thefourtheye

+0

@ HåkenLid vâng, đó là những gì nó làm, tôi nghĩ rằng OP đang hỏi * tại sao *. – jonrsharpe

Trả lời

11

list, tuple vv, không thực sự làm một kiểm tra danh tính trước khi kiểm tra bình đẳng, và hành vi này được thúc đẩy bởi 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 

Thật không may, dict s, set s, và bạn bè hoạt động bằng cách băm , vì vậy nếu bạn lộn xộn với những người bạn thực sự có thể phá vỡ chúng một cách hiệu quả.

Xem this issuethis issue cho một số lịch sử.

+1

Tôi không hiểu ý của bạn bằng cách nói "hành vi này được thúc đẩy bởi NaN". Tại sao một hành vi gây ra hành vi lạ với NaN được động cơ bởi NaN? Điều đó làm cho nó có vẻ như họ đã làm cho nó theo cách đó đặc biệt để phá vỡ NaNs. – BrenBarn

8

Nói chung, phá vỡ giả định rằng nhận dạng ngụ ý sự bình đẳng có thể phá vỡ nhiều thứ trong Python. Đúng là NaN phá vỡ giả định này, và do đó NaN phá vỡ một số thứ trong Python. Thảo luận có thể được tìm thấy trong this Python bug. Trong phiên bản tiền phát hành của Python 3.0, sự phụ thuộc vào giả định này đã được gỡ bỏ, nhưng độ phân giải của lỗi là đưa nó trở lại (ví dụ, làm cho Python 3 đưa ra hành vi tương tự như Python 2, trong đó phím tắt kiểm tra nhận dạng là làm xong). Các documentation cho Python 3 nói một cách chính xác:

Đối với các loại container như danh sách, tuple, bộ, frozenset, dict, hoặc collections.deque, khái niệm x in y tương đương với any(x is e or x == e for e in y).

Tuy nhiên, dường tài liệu cho Python 2 là không chính xác, vì nó nói:

Đối với danh sách và tuple các loại, x trong y là đúng nếu và chỉ nếu có tồn tại một chỉ số i như vậy x == y [i] là đúng.

Bạn có thể nêu ra lỗi tài liệu về điều này nếu bạn muốn, mặc dù đây là một vấn đề khá bí truyền nên tôi nghi ngờ nó sẽ cao trong danh sách ưu tiên của bất kỳ ai.

+2

Không phải là tài liệu đó ở sai 3.x cho ví dụ 'set', như các tài liệu OP? Điều đó kiểm tra băm, không nhận dạng. – jonrsharpe

+0

@jonrsharpe: Đúng vậy. Tôi đã tập trung vào hành vi bất thường của trường hợp danh sách/tuple. Bằng cách sử dụng một băm ngẫu nhiên như vậy thậm chí còn bệnh lý hơn một đối tượng không tự bình đẳng, mặc dù. – BrenBarn

+0

Bây giờ tôi đang bối rối - tôi đã chạy trên Code2Go cho iPhone, trong đó 'p in {p}' là 'True' cho 2.7.9 và 3.4.2. Bây giờ tôi đang ở trên một Yosemite MacBook, trong đó 'p in {p}' là 'False' cho 2.7.9 và 3.4.3. – jonrsharpe

Các vấn đề liên quan