使用r = range(1500, 2500)
,我x in r
對x
低于/高于/高于范圍進行了基準測驗:
1000 in r : 58 ns ± 0 ns
2000 in r : 101 ns ± 1 ns
3000 in r : 58 ns ± 0 ns
檢查 1000 比檢查 2000 快嗎?有道理,對于 1000,Python 僅在檢查范圍的下限后就知道結果,而對于 2000,它需要檢查兩個范圍。
檢查 3000 比檢查 2000 快嗎?有道理,對于 3000,Python 僅在檢查范圍的上限后才知道結果,而對于 2000,它需要檢查兩個邊界。
嘿……等一下……
它如何知道首先檢查哪個系結?如何檢查 1000 和 3000都比2000 快?
基準代碼(在線試用!):
from timeit import repeat
from statistics import mean, stdev
setup = 'r = range(1500, 2500)'
n = 10**4
for _ in range(3):
for x in 1000, 2000, 3000:
code = f'{x} in r'
ts = repeat(code, setup, number=n, repeat=100)
ts = [t/n * 1e9 for t in sorted(ts)[:10]]
print(code, ': = ns ± %d ns' % (mean(ts), stdev(ts)))
print()
uj5u.com熱心網友回復:
在 CPython 中,實作首先檢查兩個端點的值:
if (cmp1 == 1) { /* positive steps: start <= ob < stop */
cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE);
cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT);
}
如果其中一個為假,它可以立即退出:
if (cmp2 == 0 || cmp3 == 0) { /* ob outside of range */
result = 0;
goto end;
}
但是,如果兩個檢查都為真,那么它必須檢查步幅是否拒絕該值。(例如,2000 in range(1501, 2500)
為真,但2000 in range(1501, 2500, 2)
為假。)
tmp1 = PyNumber_Subtract(ob, r->start);
if (tmp1 == NULL)
goto end;
tmp2 = PyNumber_Remainder(tmp1, r->step);
if (tmp2 == NULL)
goto end;
/* result = ((int(ob) - start) % step) == 0 */
result = PyObject_RichCompareBool(tmp2, zero, Py_EQ);
正是這最后一步2000 in range(1500, 2500)
比任何一個越界檢查都慢。
(有關完整實作,請參見https://github.com/python/cpython/blob/main/Objects/rangeobject.c#L381。)
uj5u.com熱心網友回復:
如果我們查看 CPython 實作,我們可以看到該range_contains_long
函式確實包含一個快速邊界檢查,專門用于正和負步驟。
這解釋了為什么外部情況都快得多,但當然,它特定于單個實作并且可能隨時更改。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/497596.html