• 由 '' in 'abc' return True 引发的思考----Python 成员测试操作


    最近遇到判断字典中是否存在空字符串‘’,这个很好判断,直接用:‘’ in ['a','b','c'],就可以直接判断出来;但是当我对字符串使用 “in” 方法进行判断的时候,发现:‘’ in ‘abc' 仍然会返回True,对于这个问题,之前一直没有注意到过其中的原理,现在去进行探索总结一下:

    首先,查看官方文档:https://docs.python.org/2/reference/expressions.html#not-in

    文档在5.9.2中:Membership test operations中是如下说明的:

    大概翻译一下意思就是说:  

      “in” 和 “not in” 是对集合成员的检测操作,如果 x 在 集合s 中的话那么 x in s 返回True,否则返回False。x not in s 跟 x in s 判断是相反的。 成员的检测被绑定到序列;如果集合是序列,并且包含与该对象相等的元素,则对象是集合的成员。然而,对于许多其他对象类型来说,即使不是序列,但是支持成员测试也是有意义的。特别是,dict(key) 和 sets 支持成员资格测试。

    • “列表” 和 “元祖” 类型:当且仅当,'y' 中 存在一个下标 ’i‘ 使得 x is y[i] 或者 x == y[i] 成立,x in y 才会返回 True
    • “Unicode” 和 “String” 类型: 要使 'x in y' return True 成立,当且仅当,’x‘ 是 ’y‘ 的一个子字符串。有个等效的测试为 y.find(x) != -1。注:’x‘ 和 ’y‘ 不需要相同的类型;比如:u'ab' in 'abc' 也会返回True。空字符串是任何字符串的子字符串,所以,“” in 'abc' 会返回True。

      在版本2.3的时候被改变了:之前的版本,’x‘ 被要求是长度为 1 的字符串。

    • 对于用户自定义的类,分为以下三种情况考虑:
    1. 对于有__contains__()方法的用户自定义类:如果 y.__contains__(x) 为 True,则 x in y 返回True
    2. 对于没有定义__contains__()方法,但是定义了__iter__()方法:如果 有值 'z' 在对 ’y‘ 进行迭代的时候 x==z 成立,那么 x in y 会返回True,如果在迭代过程中引发异常,则会抛出该 in 异常。
    3. 对于定义了__getitem__()方法的类:当且仅当含有一个非负的整数下标 i 使得 x == y[i] 成立,那么 x in y 才会返回True,所有较低的整数索引不会引发IndexError异常。(如果有任何其他的异常出现,也会出现 in 操作出现异

        “not in” 操作和 “in” 操作产生的结果相反

    看完官方文档后,对于判断“String”类型的时候,可以通过 y.find(x) != -1 来测试其是否成立,但是这个原理是怎么来的,还是想进一步进行探究,下面查看Python2.7的源码:

    首先,猜测在源码中首先应该和对象类型有关系,然后找到发现有typeobject.c文件,浏览后发现,含有如下代码:

    SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc,
               "x.__contains__(y) <==> y in x"),

    然后接下来推测去找跟__contains__方法有关的和 上面说到的 find() 方法有关的源码:

    • 在stringlib头文件文件夹中,发现有 find.h 文件,然后在其中发现了如下函数:
    Py_LOCAL_INLINE(int)
    stringlib_contains_obj(PyObject* str, PyObject* sub)
    {
        return stringlib_find(
            STRINGLIB_STR(str), STRINGLIB_LEN(str),
            STRINGLIB_STR(sub), STRINGLIB_LEN(sub), 0
            ) != -1;
    }
    • 然后进一步去找到 stringlib_find() 的定义:
    Py_LOCAL_INLINE(Py_ssize_t)
    stringlib_find(const STRINGLIB_CHAR* str, Py_ssize_t str_len,
                   const STRINGLIB_CHAR* sub, Py_ssize_t sub_len,
                   Py_ssize_t offset)
    {
        Py_ssize_t pos;
    
        if (str_len < 0)
            return -1;
        if (sub_len == 0)
            return offset;
    
        pos = fastsearch(str, str_len, sub, sub_len, -1, FAST_SEARCH);
    
        if (pos >= 0)
            pos += offset;
    
        return pos;
    }
    • 从这里可以看到 当str_len == 0的时候,返回 offset,而 offset 已经初始化为了 0,从这里也可以大概得出:y.find(x) != -1,当 ’x‘ 为 ’空字符串‘ 时返回 0;当 ’x‘ 存在 'y' 中的时候,返回 对应的偏移量。然后接下来就会判断返回True 还是False。

    因为源码关联性的原因,接下来可以在 stringobject.c 文件中,结合 find.h 进行对于其返回值的原理查看,这里就不一一列出来了。

    源码目录:

      Objects/Stringlib/find.h

      Objects/typeobject.c

      Objects/stringobject.c

  • 相关阅读:
    Shell编程——基于IBM培训教程的总结
    flex上下固定中间滚动布局
    exe 转服务
    itextcsharp使用
    devices detect
    [转]Java api 全集 【API JDK1.6中文版】
    JavaScript 项目优化总结
    服务程序打包
    knockoutjs
    C#压缩《收藏》
  • 原文地址:https://www.cnblogs.com/ShaunChen/p/6485386.html
Copyright © 2020-2023  润新知