Python`in`与`__contains__`的功能

jos*_*ith 18 python contains python-2.7

__contains__前几天第一次在类上实现了这个方法,行为不是我的预期.我怀疑in操作员有些微妙,我不明白,我希望有人可以启发我.

在我看来,in操作符不是简单地包装对象的__contains__方法,但它也试图强制输出__contains__为boolean.例如,考虑一下这门课程

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

in运营商和对直接调用__contains__方法返回非常不同的输出:

>>> dum = Dummy()
>>> 7 in dum
True
>>> dum.__contains__(7)
[False, False]
Run Code Online (Sandbox Code Playgroud)

再次,它看起来像是in在调用__contains__,然后将结果强制转换为bool.我无法在任何地方找到这种行为,除了__contains__ 文档说的__contains__应该只返回True或者False.

我很高兴遵循这个惯例,但有人可以告诉我in和之间的确切关系__contains__吗?

结语

我决定选择@ eli-korvigo的答案,但是每个人都应该看看@ ashwini-chaudhary 关于这个bug的评论.

Eli*_*igo 13

使用来源,卢克!

让我们来看一下in运营商的实施情况

>>> 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
Run Code Online (Sandbox Code Playgroud)

如您所见,in操作员成为COMPARE_OP虚拟机指令.你可以在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(); 
Run Code Online (Sandbox Code Playgroud)

看看其中一个开关 cmp_outcome()

case PyCmp_IN:
    res = PySequence_Contains(w, v);
    if (res < 0)
         return NULL;
    break;
Run Code Online (Sandbox Code Playgroud)

在这里,我们有PySequence_Contains电话

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);
}
Run Code Online (Sandbox Code Playgroud)

这总是返回一个int(布尔值).

PS

感谢的Martijn Pieters的提供的方式查找执行in操作.


kno*_*dge 7

Python中,__contains__它的编写__contains__应该返回True或写入False.

如果返回值不是布尔值,则将其转换为布尔值.这是证明:

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

class Dummy:
    def __contains__(self, val):
        return MyValue()
Run Code Online (Sandbox Code Playgroud)

现在写在shell中:

>>> dum = Dummy()
>>> 7 in dum
__bool__ function ran
True
Run Code Online (Sandbox Code Playgroud)

bool()非空目录的回报True.

编辑:

这是唯一的文档__contains__,如果你真的想看到精确的关系,你应该考虑查看源代码,虽然我不确定究竟在哪里,但它已经得到了答案.在比较文档中写道:

但是,这些方法可以返回任何值,因此如果在布尔上下文中使用比较运算符(例如,在if语句的条件下),Python将对值调用bool()以确定结果是true还是false.

所以你可以猜测它与它相似__contains__.

  • 我认为`“__bool__函数运行”应该是`“__bool__函数运行”` (2认同)