Python`in`与`__contains__`的功能 [英] Functionality of Python `in` vs. `__contains__`

查看:72
本文介绍了Python`in`与`__contains__`的功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前几天我第一次在类上实现了 __ contains __ 方法,但这种行为并非我所期望的。我怀疑 in 中有些细微之处我不理解的运算符,我希望有人能启发我。

I implemented the __contains__ method on a class for the first time the other day, and the behavior wasn't what I expected. I suspect there's some subtlety to the in operator that I don't understand and I was hoping someone could enlighten me.

在我看来,运算符不仅可以包装对象的 __ contains __ 方法,还可以尝试强制输出 __ contains __ 转换为布尔值。例如,考虑类

It appears to me that the in operator doesn't simply wrap an object's __contains__ method, but it also attempts to coerce the output of __contains__ to boolean. For example, consider the class

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

in 运算符和对 __ contains __ 方法的直接调用将返回非常不同的输出:

The in operator and a direct call to the __contains__ method return very different output:

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

再次,看起来< 中的code>调用 __ contains __ ,但随后将结果强制为 bool 。除了 __ contains __ 文档说, __ contains __ 应该只返回 True False

Again, it looks like in is calling __contains__ but then coercing the result to bool. I can't find this behavior documented anywhere except for the fact that the __contains__ documentation says __contains__ should only ever return True or False.

我很高兴遵循惯例,但是有人可以告诉我<$与之间的确切关系吗? c $ c> in __ contains __

I'm happy following the convention, but can someone tell me the precise relationship between in and __contains__?

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

I decided to choose @eli-korvigo answer, but everyone should look at @ashwini-chaudhary comment about the bug, below.

推荐答案

使用源代码,卢克!

我们来追溯运算符的实现中

Let's trace down the in operator implementation

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

如您所见, 运算符成为 COMPARE_OP 虚拟机指令。您可以在 ceval.c

As you can see, the in operator becomes the COMPARE_OP virtual machine instruction. You can find that in 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(); 

看看 cmp_outcome()

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

在这里,我们有 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);
}

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

That always returns an int (a boolean).

PS

感谢Martijn Pieters提供了方法来找到 in 运算符的实现。

Thanks to Martijn Pieters for providing the way to find the implementation of the in operator.

这篇关于Python`in`与`__contains__`的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆