2017-01-31 17 views
3

私は、次の変数を定義するとしますPythonのリストクラスの__contains__メソッドはどのように実装されていますか?

mode = "access" 
allowed_modes = ["access", "read", "write"] 

は、私は現在、

assert any(mode == allowed_mode for allowed_mode in allowed_modes) 

で型チェックステートメントを持っているしかし、私が単に

assert mode in allowed_modes 

でこれを置き換えることができているようですThiefMasterの答えはPython List Class __contains__ Method Functionalityで、これらの2つは同等でなければなりません。これは本当に事実ですか?そして、どうすれば簡単にPythonのソースコードを調べることができますか?

+0

私はこれを発見しました:https://github.com/python/cpython/blob/master/Objects/listobject.c行402を参照してください。 – 0Tech

+0

はい、同等です。第2の(より短い)バージョンは非常にわずかに速いはずです。 [contains](https://github.com/python-git/python/blob/master/Objects/listobject.c#L430)のソースとリストイテレータの[next](https:// github)を見ることができます.com/python-git/python/blob/master/Objects/listobject.c#L2872)。 – wildwilhelm

答えて

7

いいえ、これらは同等ではありません。たとえば:そのようなリスト、タプル、セット、frozensetの、辞書、またはcollections.dequeなどのコンテナタイプの

は、表現x in yは次のとおりです。

>>> mode = float('nan') 
>>> allowed_modes = [mode] 
>>> any(mode == allowed_mode for allowed_mode in allowed_modes) 
False 
>>> mode in allowed_modes 
True 

は、この文を含め、詳細はMembership test operationsを参照してください。 any(x is e or x == e for e in y)に相当します。

+0

うわー...いつも新しいことをここで学ぶ。どうもありがとうございます。 –

5

PythonリストはCコードで定義されています。

あなたはcode in the repositoryを見て、それを検証することがあります。

static int 
list_contains(PyListObject *a, PyObject *el) 
{ 
    Py_ssize_t i; 
    int cmp; 

    for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i) 
     cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i), 
              Py_EQ); 
    return cmp; 
} 

それは、このコードは、リスト内の項目をループすることを確認し、最初の平等elPyList_GET_ITEM(a, i)リターン1間(Py_EQ)の比較を停止するにはかなりまっすぐ進むのです

3

anyには、追加の関数呼び出し、ジェネレータ式などが必要なため、同等の機能はありません。

>>> mode = "access" 
>>> allowed_modes =["access", "read", "write"] 
>>> 
>>> def f1(): 
... mode in allowed_modes 
... 
>>> def f2(): 
... any(mode == x for x in allowed_modes) 
... 
>>> 
>>> 
>>> import dis 
>>> dis.dis 
dis.dis(   dis.disassemble( dis.disco(  dis.distb(  
>>> dis.dis(f1) 
    2   0 LOAD_GLOBAL    0 (mode) 
       3 LOAD_GLOBAL    1 (allowed_modes) 
       6 COMPARE_OP    6 (in) 
       9 POP_TOP 
      10 LOAD_CONST    0 (None) 
      13 RETURN_VALUE 
>>> dis.dis(f2) 
    2   0 LOAD_GLOBAL    0 (any) 
       3 LOAD_CONST    1 (<code object <genexpr> at 0x7fb24a957540, file "<stdin>", line 2>) 
       6 LOAD_CONST    2 ('f2.<locals>.<genexpr>') 
       9 MAKE_FUNCTION   0 
      12 LOAD_GLOBAL    1 (allowed_modes) 
      15 GET_ITER 
      16 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      19 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      22 POP_TOP 
      23 LOAD_CONST    0 (None) 
      26 RETURN_VALUE 
>>> 

は、これは自分自身のメソッドのためのPythonのソースよりも有益であるが、hereはリストの__contains__の源であり、ループは、おそらくPythonのループよりも高速になりますCです。

一部のタイミング番号でこれが確認されています。

>>> import timeit 
>>> timeit.timeit(f1) 
0.18974408798385412 
>>> timeit.timeit(f2) 
0.7702703149989247 
>>> 
+0

"等価でない"は、 "等価"の定義方法によって異なります。機能的には、両方の解決策は同じ結果をもたらすという点で同等である。 –

+0

真実ですが、私の意図はできるだけ多くの点で相違点を見つけて強調することでした。その後、関連性のないものを破棄して、それと同等かそれ以外のものを検討することができます。私にとっては、この状況でf2ではなくf1を使用すべきであることは明らかです。 –

+0

私はあなたの意図をよく理解していました;) - 両方のソリューションが同じ結果をもたらすことを明確にしたかったのです(OPは「同等」を定義していなかったため)。もちろん、封じ込めテストは、(最速であったように)明らかなピジョンソニックソリューションです。 –

関連する問題