我认为in
只是__contains__
In [1]: li = [1, 2, 3, 4, 5]
In [2]: 4 in li
Out[2]: True
In [3]: li.__contains__(4)
Out[3]: True
然而in
快2倍
In [4]: %timeit 4 in li
10000000 loops, best of 3: 103 ns per loop
In [5]: %timeit li.__contains__(4)
The slowest run took 10.06 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 213 ns per loop
你能解释两者之间的差异吗?为什么in
更快?
答案 0 :(得分:5)
可能与{}
比dict()
快的原因相同。方法调用引入了额外的开销:
>>> from dis import dis
>>> li = [1, 2, 3, 4, 5]
>>> c = lambda: 4 in li
>>> d = lambda: li.__contains__(4)
>>> dis(c)
1 0 LOAD_CONST 1 (4)
3 LOAD_GLOBAL 0 (li)
6 COMPARE_OP 6 (in)
9 RETURN_VALUE
>>> dis(d)
1 0 LOAD_GLOBAL 0 (li)
3 LOAD_ATTR 1 (__contains__)
6 LOAD_CONST 1 (4)
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 RETURN_VALUE
在后一种情况下请参阅CALL_FUNCTION
。
答案 1 :(得分:2)
这个答案有点建立在@MosesKoledoye的解释之上,但没有任何反汇编的源代码。
.__contains__
比使用in
慢的原因有多种原因:
每次使用.
访问方法/函数/类/属性时,都会在类/实例字典中引入查找。这引入了一些开销。
对于自定义类,对in
的调用会转换为.__contains__
(或.__iter__
),但对于所有python内置函数来说,这不一定都是正确的。 Python列表以C语言实现,因此something in list
可以转换为C函数,而无需调用__contains__
或__iter__
。
in list
直接转发到C代码(COMPARE_OP
),这比访问python函数然后调用C代码(CALL_FUNCTION
)快得多。
一般情况下:对于使用像in
这样的文字的python内置函数可能会更快,但这对于自定义类来说(通常)不正确。 python中的大多数数据模型都适用于自定义类。 list.__contains__
可能只是为了适应数据模型约定而实现,而不是因为它需要或更快。
答案 2 :(得分:2)
在Python的标准实现中,Button
并没有直接使用in
。 __contains__
实际上在结构中使用C级in
函数指针来表示对象的类型。对于在Python中实现sq_contains
的类型,__contains__
指向查找并调用sq_contains
方法的函数,但对于在C中实现__contains__
的类型,{ {1}}直接指向方法的C实现。
由于__contains__
是在C中实现的,sq_contains
可以直接调用C实现,而不需要经过Python方法查找和Python函数调用的开销。 list.__contains__
必须执行Python方法查找和Python函数调用,因此速度要慢得多。
如果要查看4 in li
涉及的代码路径,可以在字节码评估循环中从COMPARE_OP
开始跟随调用层次结构。您会看到它使用cmp_outcome
:
li.__contains__(4)
cmp_outcome
使用PySequence_Contains
:
4 in li
PySequence_Contains
查找代表列表类型的C结构的TARGET(COMPARE_OP)
{
w = POP();
v = TOP();
if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
...
}
else {
slow_compare:
x = cmp_outcome(oparg, v, w);
}
字段的static PyObject *
cmp_outcome(int op, register PyObject *v, register PyObject *w)
{
int res = 0;
switch (op) {
...
case PyCmp_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
break;
字段:
sq_contains
This field存储一个函数指针list_contains
,C函数实现列表包含检查。 Python不得不执行任何dict查找来查找方法,或者分配方法对象来表示方法,或者构建一个参数元组以传递给方法。