先日初めてクラスに__contains__
メソッドを実装しましたが、その動作は期待したものではありませんでした。 in
演算子には理解できない微妙な点があると思います。誰かに教えてもらえたらと思っていました。
in
演算子は、オブジェクトの__contains__
メソッドを単純にラップするのではなく、__contains__
の出力をブール値に強制しようとするように見えます。たとえば、クラスを考えてみましょう
class Dummy(object):
def __contains__(self, val):
# Don't perform comparison, just return a list as
# an example.
return [False, False]
in
演算子と__contains__
メソッドへの直接呼び出しは、非常に異なる出力を返します。
>>> dum = Dummy()
>>> 7 in dum
True
>>> dum.__contains__(7)
[False, False]
繰り返しますが、in
が__contains__
を呼び出しているようですが、結果をbool
に強制変換しています。 __contains__
documentation が__contains__
がTrue
またはFalse
のみを返すように指示しているという事実を除いて、この動作が文書化されている場所はありません。
規約を守って満足していますが、in
と__contains__
の正確な関係を誰かに教えてもらえますか?
@ eli-korvigoの回答を選択することにしましたが、以下の bug については、@ ashwini-chaudhary comment をご覧ください。
ソース、ルークを使用してください!
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
_
ご覧のとおり、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();
_
cmp_outcome()
のスイッチの1つを見てください。
_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
(ブール値)を返します。
追伸.
in
演算子の実装を見つけるために way を提供してくれたMartijn Pietersに感謝します。
___contains__
_ のPythonリファレンスでは、___contains__
_がTrue
またはFalse
を返すように書かれています。
戻り値がブール値でない場合は、ブール値に変換されます。ここに証明があります:
_class MyValue:
def __bool__(self):
print("__bool__ function ran")
return True
class Dummy:
def __contains__(self, val):
return MyValue()
_
今シェルで書いてください:
_>>> dum = Dummy()
>>> 7 in dum
__bool__ function ran
True
_
空でないリストのbool()
はTrue
を返します。
編集:
これは___contains__
_の唯一のドキュメントです。正確な関係を確認したい場合は、ソースコードを調べることを検討してください。正確な場所はわかりませんが、すでに回答されています。 比較のためのドキュメント と書かれています:
ただし、これらのメソッドは任意の値を返すことができるため、比較演算子がブールコンテキストで使用されている場合(たとえば、
if
ステートメントの条件で)、Pythonは- bool() 値に基づいて、結果がtrueかfalseかを判断します。
したがって、これは___contains__
_と似ていると推測できます。