python中有一个很神奇的关键字in,在for循环中会用到for x in range,在判断中会用到if x in data,在语意表达上更接近人类的理解范畴,使得代码更人性化可理解。作为一个关键字,in在不同位置时所蕴含的语意看起来是相同的,都是以对in之后的变量做一个遍历或检查,表示接下来的操作是基于in之后的变量的,但实际上在底层实现中in在不同位置所调用的的方法是不同的。

在for循环中,in会调用其后面的对象中的__iter__()函数,表示将其作为一个迭代器遍历,也可以认为是实现了类似next()的方法。因此当想对某个对象用for in方式进行操作时,只需实现该对象的__iter__()函数,使用yield迭代出返回结果或直接返回list这样的数据结构即可。

在if in 的情况下,表示判断某个元素是否在in之后的变量中存在。其实际上是调用了对象的__contains__()函数,一般返回一个bool值,其参数是一个item,一般用于检查参数item在该对象中是否存在,或是其他判断语意。

class a(object):
    d='ddd'
    def __contains__(self):
        if self.d:return True
b=a()
print(b.contains('d')) # error
print(contains(b,'d'))  # error

像所有特殊方法(以__开头和结尾的"魔术名称")一样,__contains__并不意味着可以直接调用(在非常特殊的情况下,例如对超类的调用)方法被称为内置函数和运算符的操作的一部分。在__contains__的情况下,所讨论的运算符是in-"包含检查"运算符。

使用类a进行演示(除了修正拼写错误,并使用True代替True),并以b作为其实例,print( ‘x’ in b)将打印True——因为b总是返回True(self.d是一个非空字符串,所以为true),所以对b的其他任何容纳检查也将进行检查。

# __contains__方法定义当类实例出现在in和not in运算符的右侧时的行为。
class a(object):
    d = 'ddd'
    def __contains__(self, m):
        if self.d: 
            return True
b = a()
>>> 'd' in b
True

if self.d:return true

比较特别的是,我们知道当对象实现了__getitem__()函数,那么就可以使val[index]的语法,利用下标访问对象元素,实际上__getitem__()同样也用于if in的fallback机制,当用户没有实现或调用失败对象的__contains__()函数时,python就会在if in的情况下调用__getitem__()方法。