Python`in`与`__contains__`的功能 [英] Functionality of Python `in` vs. `__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屋!