2016-07-23 29 views
10

Tôi đã thực hiện phương thức __contains__ trên lớp lần đầu tiên vào ngày khác và hành vi không phải là những gì tôi mong đợi. Tôi nghi ngờ có một số tinh tế để các nhà điều hành in mà tôi không hiểu và tôi đã hy vọng một người nào đó có thể soi sáng cho tôi.Chức năng của Python `in` so với` __contains__`

Dường như với tôi rằng toán tử in không đơn giản quấn phương thức __contains__ của đối tượng, nhưng nó cũng cố gắng ép buộc đầu ra của __contains__ thành boolean. Ví dụ, hãy xem xét các lớp

class Dummy(object): 
    def __contains__(self, val): 
     # Don't perform comparison, just return a list as 
     # an example. 
     return [False, False] 

Nhà điều hành in và một cuộc gọi trực tiếp đến __contains__ phương thức hoàn trả sản lượng rất khác nhau:

>>> dum = Dummy() 
>>> 7 in dum 
True 
>>> dum.__contains__(7) 
[False, False] 

Một lần nữa, nó trông giống như in đang kêu gọi __contains__ nhưng sau đó ép buộc kết quả đến bool. Tôi không thể tìm thấy hành vi này được ghi lại ở bất kỳ đâu ngoại trừ thực tế là __contains__documentation nói __contains__ chỉ nên trả lại True hoặc False.

Tôi hài lòng theo quy ước, nhưng ai đó có thể cho tôi biết mối quan hệ chính xác giữa in__contains__?

Epilogue

tôi quyết định chọn câu trả lời @ eli-korvigo, nhưng tất cả mọi người nên nhìn vào @ Ashwini-Chaudhary comment về bug, bên dưới.

+0

Vì trả về phương thức chứa của bạn tương đương với bool ([Sai, False]) – x1Mike7x

+2

Lỗi liên quan: ['in' phải nhất quán với giá trị trả về' __contains__'] (https://bugs.python.org/issue16011) –

+1

@AshwiniChaudhary: Bạn có thể viết nhận xét này làm câu trả lời không? Chỉ cần một lót hoặc như vậy là tốt. Tôi chưa bao giờ thấy báo cáo lỗi này và nó trả lời chính xác câu hỏi của tôi. Tôi không quan tâm nhiều đến việc triển khai cụ thể của 'in' khi tôi quan tâm đến lý do thiết kế và thiếu tài liệu rõ ràng. Nếu bạn đăng câu trả lời này, tôi sẽ chọn câu trả lời của bạn làm câu trả lời. –

Trả lời

8

Sử dụng nguồn, Luke!

Hãy theo dõi xuống thi in hành

>>> import dis 
>>> class test(object): 
...  def __contains__(self, other): 
...   return True 

>>> def in_(): 
...  return 1 in test() 

>>> dis.dis(in_) 
    2   0 LOAD_CONST    1 (1) 
       3 LOAD_GLOBAL    0 (test) 
       6 CALL_FUNCTION   0 (0 positional, 0 keyword pair) 
       9 COMPARE_OP    6 (in) 
       12 RETURN_VALUE 

Như bạn có thể thấy, các nhà điều hành in trở thành hướng dẫn máy ảo COMPARE_OP. Bạn có thể thấy rằng trong ceval.c

TARGET(COMPARE_OP) 
    w = POP(); 
    v = TOP(); 
    x = cmp_outcome(oparg, v, w); 
    Py_DECREF(v); 
    Py_DECREF(w); 
    SET_TOP(x); 
    if (x == NULL) break; 
    PREDICT(POP_JUMP_IF_FALSE); 
    PREDICT(POP_JUMP_IF_TRUE); 
    DISPATCH(); 

Hãy xem một trong những công tắc trong cmp_outcome()

case PyCmp_IN: 
    res = PySequence_Contains(w, v); 
    if (res < 0) 
     return NULL; 
    break; 

Ở đây chúng ta có PySequence_Contains gọi

int 
PySequence_Contains(PyObject *seq, PyObject *ob) 
{ 
    Py_ssize_t result; 
    PySequenceMethods *sqm = seq->ob_type->tp_as_sequence; 
    if (sqm != NULL && sqm->sq_contains != NULL) 
     return (*sqm->sq_contains)(seq, ob); 
    result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS); 
    return Py_SAFE_DOWNCAST(result, Py_ssize_t, int); 
} 

Đó luôn luôn trả về một int (một boolean).

P.S.

Nhờ Martijn Pieters cung cấp số way để tìm cách triển khai toán tử in.

+0

Cảm ơn bạn đã trả lời kỹ lưỡng, nhưng tôi đã tìm kiếm thêm lý do đằng sau thiết kế và thiếu tài liệu rõ ràng hơn việc thực hiện 'in'. Tôi đang upvoting câu trả lời của bạn anyway bởi vì nó là thông tin hữu ích. –

+0

@ joshua.r.smith Tôi đoán, trong trường hợp này việc triển khai có liên quan trực tiếp đến lý do. Về cơ bản, đây là cách Python-C API được hình thành. Đối với việc thiếu tài liệu, các tài liệu không thực sự tham chiếu 'True' hoặc' False', chúng chỉ nói rằng '__cointains__' phải trả về một cái gì đó đúng hoặc sai (tức là có thể được đánh giá là' True' hoặc 'False'). Bạn có thể thấy trong suốt các tài liệu, rằng chúng sử dụng một cách rõ ràng 'True' và' False', nơi quan trọng. Dù sao, họ có thể đã viết nó ít mơ hồ hơn, vì vậy bạn có thể nộp báo cáo bản vá tài liệu. –

5

Trong Python reference for __contains__ nó được viết là __contains__ phải trả lại True hoặc False.

Nếu giá trị trả về không phải là boolean, nó được chuyển thành boolean. Đây là bằng chứng:

class MyValue: 
    def __bool__(self): 
     print("__bool__ function runned") 
     return True 

class Dummy: 
    def __contains__(self, val): 
     return MyValue() 

Bây giờ viết trong vỏ:

>>> dum = Dummy() 
>>> 7 in dum 
__bool__ function runned 
True 

bool() danh sách rỗng trả True.

Edit:

Nó chỉ tài liệu cho __contains__, nếu bạn thực sự muốn nhìn thấy mối quan hệ chính xác bạn nên xem xét nhìn vào mã nguồn mặc dù tôi không chắc chắn nơi chính xác, nhưng nó đã được trả lời. Trong documentation for comparison nó được viết:

Tuy nhiên, những phương pháp này có thể trở lại bất kỳ giá trị, vì vậy nếu các toán tử so sánh được sử dụng trong một bối cảnh Boolean (ví dụ, trong điều kiện của một câu lệnh if), Python sẽ gọi bool() trên giá trị để xác định xem kết quả là đúng hay sai.

Vì vậy, bạn có thể đoán rằng nó tương tự với __contains__.

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