Python 3 で range(0) == range(2, 2, 2) が True になるのはなぜですか? 質問する

Python 3 で range(0) == range(2, 2, 2) が True になるのはなぜですか? 質問する

Python 3 では、異なる値で初期化された範囲が互いに等しいと判断されるのはなぜですか?

インタープリターで次のコマンドを実行すると:

>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True

結果は ですTrue。なぜそうなるのでしょうか?range異なるパラメータ値を持つ 2 つの異なるオブジェクトが同等として扱われるのはなぜでしょうか?

ベストアンサー1

オブジェクトはrange特別です:

Pythonは比較しますrangeオブジェクトとしてシーケンスつまり、比較は評価しないどうやってそれらは与えられたシーケンスを表すのではなく、彼らが代表する。

startstopパラメータが完全に異なるという事実はstepここでは何ら影響を及ぼさない。展開するとすべて空のリストを表す:

たとえば、最初のrangeオブジェクト:

list(range(0))  # []

そして2番目のrangeオブジェクト:

list(range(2, 2, 2)) # []

どちらも空のリストを表す2つの空のリストは等しい(True)のでrange代表する彼ら。

その結果、全く異なる見ている rangeオブジェクトが同じシーケンスを表す場合は、比較する等しい:

range(1, 5, 100) == range(1, 30, 100) 

どちらも 1 つの要素を持つリストを表す[1]ため、これら 2 つは等しいと評価されます。


いいえ、rangeオブジェクトは本当に特別:

ただし、比較では評価されないことに注意してくださいどうやってこれらは比較の結果のシーケンスを表します達成することができます使用して専らの値はstart、オブジェクトのstepとともに、比較の速度に関して非常に興味深い意味を持ちます。lenrange

r0 = range(1, 1000000)    
r1 = range(1, 1000000)

l0 = list(r0)    
l1 = list(r1)

範囲を超高速で比較します:

%timeit r0 == r1
The slowest run took 28.82 times longer than the fastest. This could mean that an intermediate result is being cached 
10000000 loops, best of 3: 160 ns per loop

一方、リストは..

%timeit l0 == l1
10 loops, best of 3: 27.8 ms per loop

うん..


としてフォロー注意してください、これはPython 3のrangeオブジェクトにのみ適用されます。Python 2はrange()リストを返す単純な関数ですが、2.x xrangeオブジェクトには比較機能がありません(これらだけではありません。)rangeです。

見る@ajcr の回答Python 3オブジェクトのソースコードから直接引用しますrange。そこには、2つの異なる範囲の比較が実際に何を意味するかが文書化されています。シンプルで素早い操作。range_equals機能は、range_richcompare関数およびケースに割り当てられEQNEtp_richcomparePyRange_Type型用のスロット

の実装は、range_equalsここに追加するとかなり読みやすくなると思います (シンプルで良いため)。

/* r0 and r1 are pointers to rangeobjects */

/* Check if pointers point to same object, example:    
       >>> r1 = r2 = range(0, 10)
       >>> r1 == r2
   obviously returns True. */
if (r0 == r1)
    return 1;

/* Compare the length of the ranges, if they are equal 
   the checks continue. If they are not, False is returned. */
cmp_result = PyObject_RichCompareBool(r0->length, r1->length, Py_EQ);
/* Return False or error to the caller
       >>> range(0, 10) == range(0, 10, 2)  
   fails here */
if (cmp_result != 1)
    return cmp_result;

/* See if the range has a lenght (non-empty). If the length is 0
   then due to to previous check, the length of the other range is 
   equal to 0. They are equal. */
cmp_result = PyObject_Not(r0->length);
/* Return True or error to the caller. 
       >>> range(0) == range(2, 2, 2)  # True
   (True) gets caught here. Lengths are both zero. */
if (cmp_result != 0)
    return cmp_result;

/* Compare the start values for the ranges, if they don't match
   then we're not dealing with equal ranges. */
cmp_result = PyObject_RichCompareBool(r0->start, r1->start, Py_EQ);
/* Return False or error to the caller. 
   lens are equal, this checks their starting values
       >>> range(0, 10) == range(10, 20)  # False
   Lengths are equal and non-zero, steps don't match.*/
if (cmp_result != 1)
    return cmp_result;

/* Check if the length is equal to 1. 
   If start is the same and length is 1, they represent the same sequence:
       >>> range(0, 10, 10) == range(0, 20, 20)  # True */
one = PyLong_FromLong(1);
if (!one)
    return -1;
cmp_result = PyObject_RichCompareBool(r0->length, one, Py_EQ);
Py_DECREF(one);
/* Return True or error to the caller. */
if (cmp_result != 0)
    return cmp_result;

/* Finally, just compare their steps */
return PyObject_RichCompareBool(r0->step, r1->step, Py_EQ);

私自身のコメントもここにいくつか載せました。@ajcr の回答Python の同等のもの。

おすすめ記事