操作符详解之成员测试(Python)

转载请注明出处:https://www.jianshu.com/u/5e6f798c903a

参考文档:Membership test operations

成员测试使用 innot in 作为操作符。如果 x 是 s 中的一员,x in s 便会返回 True ;否则返回 Falsex not in sx in s 的真值相反。所有内置序列(sequences)和集合(set)类型都支持成员测试;字典也支持该操作,不过字典将键(key)作为测试对象。对于容器类型(如 list、tuple、set、frozenset、dict 或 collections.deque),表达式 x in y 相当于 any(x is e or x == e for e in y)

另外,对于 string 和 bytes 类型,仅当 x 是 y 的子串(substring)时,x in y 会返回 True。在这种情况下,成员测试等效于 y.find(x) != -1。由于空字符串被视作任何字符的子串,所以 "" in "abd" 将会返回 True

对某个对象进行成员测试时,依据对象所持方法的不同,可分为如下三种情况:

  • 第一种情况是在用户(user-defined)定义类中已定义了 __contains__() 方法。那么在进行成员测试时,便会调用该方法。x in yy.__contains__(x) 的真值相同,也就是说如果后者返回 True,前者也会返回 True

    class Fib(object):
        def __reset(self):
            self.a = 1
            self.b = 1
    
        def __init__(self):
            self.__reset()
    
        def __contains__(self, item):
            print("调用 __contains__")
            self.__reset()
            while True:
                self.a, self.b = self.b, self.a + self.b
                if item == self.a:
                    return True
                elif item < self.a:
                    return False
    
        def __reset(self):
            self.a = 1
            self.b = 1
    
    
    a_fib = Fib()
    print(3 in a_fib)
    

    输出:

    调用 __contains__
    True
    
  • 第二种情况是在用户定义类中并没有定义 __contains__(),但定义了 __iter__()。那么在执行 x in y 时,便会通过 __iter__() 对 y 进行迭代,如果在迭代过程中产生了与 x 相等的值,就会返回 True。在迭代过程中,只要确定存在与 x 相等的值,便会停止迭代,不会执行完全部的迭代过程。如果在迭代过程中引发了异常,则如同在成员测试中引发的异常一样。

    class Fib(object):
        def __reset(self):
            self.a = 1
            self.b = 1
    
        def __init__(self):
            self.__reset()
    
        def __iter__(self):
            """测试该方法前,请删除__contains__"""
            print("调用 __iter__()")
            self.__reset()
            return self
    
        def __next__(self):
            self.a, self.b = self.b, self.a + self.b  # 计算下一个值
            if self.a > 100000:  # 退出循环的条件
                print("停止迭代")
                raise StopIteration()
            return self.a  # 返回下一个值
        
    a_fib = Fib()
    print(3 in a_fib)
    

    输出:

    调用 __iter__()
    True
    
  • 第三种情况是 __contains__()__iter__() 都没有定义,但定义了 __getitem__()。那么在执行 x in y 时,便会通过 __getitem__() 对 y 中各项逐一索引,仅当存在 x == y[i]( i 为非负整数)时,会返回 True。如果在迭代过程中引发了异常,则如同在成员测试中引发的异常一样。
    tips:定义了 __getitem__() ,但没有定义 __iter__()时,会尝试使用旧式迭代协议,也就是通过 __getitem__() 逐一索引各个元素的方式,进行迭代。另外,我没有看懂文档中的这句话:”all lower integer indices do not raise IndexError exception. “

    class Fib(object):
        def __reset(self):
            self.a = 1
            self.b = 1
    
        def __init__(self):
            self.__reset()
    
        def __getitem__(self, item):
            """测试该方法前,请删除__contains__和__iter__"""
            self.__reset()
            print("调用 __getitem__")
            for x in range(item):
                self.a, self.b = self.b, self.a + self.b
                if self.a > 100000:  # 退出循环的条件
                    print("停止迭代")
                    raise StopIteration()
            return self.a
        
    a_fib = Fib()
    print(3 in a_fib)
    

    输出:

    调用 __getitem__
    True
    

小结:综合以上三种情况,可见成员测试会优先使用 __contains__,如果没有定义该方法才会使用 __iter__(),如果前面两个方法都未定义才会尝试 __getitem__

    原文作者:Orca_J35
    原文地址: https://www.jianshu.com/p/50b9137a5066
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞