大家在编写Python代码时经常会用in操作符做包含测试,例如:
>>> 2 in (1, 2, 3)
True
这里分析一下in测试的内部实现原理。
in测试会逐一使用被测试对象的__contains__、__iter__、__getitem__内置函数,优先级是__contains__ > __iter__ > __getitem__。
也就是说如果对象定义了__contains__函数,就利用__contains__进行测试,否则就利用__iter__,以此类推。
咱们先来看个利用__contains__的例子:
>>> class InTest():
def __contains__(self, key):
print('In test for key %s' % key)
return key in [1, 2, 3]
>>> in_test_obj = InTest()
>>> 2 in in_test_obj
In test for key 2
True
>>> 5 in in_test_obj
In test for key 5
False
上面的例子可以看出,如果对象定义了__contains__内置函数,那么in测试的结果直接由__contains__的返回值确定。
再来看一下利用__iter__的例子:
>>> class InTest():
def __iter__(self):
print('In __iter__ of %s' % self.__class__)
return iter([1, 2, 3])
>>> in_test_obj = InTest()
>>> 1 in in_test_obj
In __iter__ of __main__.InTest
True
>>> 2 in in_test_obj
In __iter__ of __main__.InTest
True
>>> 10 in in_test_obj
In __iter__ of __main__.InTest
False
可以看到,每次in测试时,实际是先通过对象的__iter__获取一个迭代器,然后循环比较迭代值。匹配成功则返回True。
最后看一下利用__getitem__的例子:
>>> class InTest():
def __getitem__(self, index):
print('In __getitem__ for %s' % index)
return [1, 2, 3][index]
>>> in_test_obj = InTest()
>>> 1 in in_test_obj
In __getitem__ for 0
True
>>> 2 in in_test_obj
In __getitem__ for 0
In __getitem__ for 1
True
>>> 3 in in_test_obj
In __getitem__ for 0
In __getitem__ for 1
In __getitem__ for 2
True
>>> 4 in in_test_obj
In __getitem__ for 0
In __getitem__ for 1
In __getitem__ for 2
In __getitem__ for 3
False
可以看到,in测试是通过向__getitem__逐一传入从0开始的索引值,用被测试值去匹配__getitem__的返回值。
如果匹配成功,则整个in测试返回True;否则,直到索引值超出序列有效范围,抛出IndexError异常,in测试返回False。