Skip to content

Python 内核源码解析:非真非假,那么是什么?

背景

上一篇提到 Python 中 if 条件结果值判断出现了非真 (True) 非假 (False) 的判断,具体代码见指令POP_JUMP_IF_FALSEPOP_JUMP_IF_TRUE。看到这样的设计,是不是有点困惑,下面就让我来分析一下。

抽象意义上确实只有真或假,但是在 Python 中(其它弱类型语言也是同样的一个原理),True 或者 False 在判断值是否相等时,

Python 中对于值的判断,真或假是两个分类,分别管理了一类型的值。比如 0,空字符串,空列表,空字典等等,这些值都假。像其它长度大于 0 的字符串,数组、字典,其值是真(限 if 判断)。

POP_JUMP_IF_FALSE 和 POP_JUMP_IF_TRUE 指令解析

POP_JUMP_IF_FALSE

回顾一下指令POP_JUMP_IF_FALSE,如果栈顶元素为假,则跳转到指定的位置,否则继续执行下一条指令。

c
    case TARGET(POP_JUMP_IF_FALSE): {       // pop TOS and jump if false
        PREDICTED(POP_JUMP_IF_FALSE);
        PyObject *cond = POP();
        int err;
        if (Py_IsTrue(cond)) {      // 如果条件为真,则不跳转
            Py_DECREF(cond);
            DISPATCH();
        }
        if (Py_IsFalse(cond)) {     // 如果条件为假,则跳转到指定位置
            Py_DECREF(cond);
            JUMPTO(oparg);
            CHECK_EVAL_BREAKER();
            DISPATCH();
        }
        err = PyObject_IsTrue(cond);
        Py_DECREF(cond);
        if (err > 0)
            ;
        else if (err == 0) {
            JUMPTO(oparg);              // 跳转到指定位置,oparg 是指定位置的偏移量
            CHECK_EVAL_BREAKER();
        }
        else
            goto error;
        DISPATCH();
    }

POP_JUMP_IF_TRUE

POP_JUMP_IF_TRUE指令代码如下,如果栈顶元素为真,则跳转到指定的位置,否则继续执行下一条指令。

c
    case TARGET(POP_JUMP_IF_TRUE): {        // pop TOS and jump if true
        PREDICTED(POP_JUMP_IF_TRUE);
        PyObject *cond = POP();
        int err;
        if (Py_IsFalse(cond)) {             // 如果条件为假,则不跳转
            Py_DECREF(cond);
            DISPATCH();
        }
        if (Py_IsTrue(cond)) {              // 如果条件为真,则跳转到指定位置
            Py_DECREF(cond);
            JUMPTO(oparg);
            CHECK_EVAL_BREAKER();
            DISPATCH();
        }
        err = PyObject_IsTrue(cond);
        Py_DECREF(cond);
        if (err > 0) {
            JUMPTO(oparg);                  // 如果条件为真,则跳转到指定位置
            CHECK_EVAL_BREAKER();
        }
        else if (err == 0)
            ;
        else
            goto error;
        DISPATCH();
    }

POP_JUMP_IF_FALSEPOP_JUMP_IF_TRUE代码基本一致,只是判断条件的顺序不同,POP_JUMP_IF_FALSE先判断条件是否为真,如果为真则不跳转,如果为假则跳转到指定位置;POP_JUMP_IF_TRUE先判断条件是否为假,如果为假则不跳转,如果为真则跳转到指定位置。

起到关键作用的是Py_IsTruePyObject_IsTrue。接下来就分析一下这两块带代码的实现

Py_IsTrue 是宏定义

Py_FalsePy_True是全局定义的值,它们分别是_Py_FalseStruct_Py_TrueStruct结构的内存地址,程序启动之后它们的内存地址是不变的。Py_Is(x, y) 其实就是比较两个对象的内存地址是否相等,如果相等则返回 1,否则返回 0。

c
/* Use these macros */
#define Py_False ((PyObject *) &_Py_FalseStruct)
#define Py_True ((PyObject *) &_Py_TrueStruct)

// Test if an object is the True singleton, the same as "x is True" in Python.
PyAPI_FUNC(int) Py_IsTrue(PyObject *x);
#define Py_IsTrue(x) Py_Is((x), Py_True)

// Test if an object is the False singleton, the same as "x is False" in Python.
PyAPI_FUNC(int) Py_IsFalse(PyObject *x);
#define Py_IsFalse(x) Py_Is((x), Py_False)

// Test if the 'x' object is the 'y' object, the same as "x is y" in Python.
PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y);
#define Py_Is(x, y) ((x) == (y))

/* The objects representing bool values False and True */

struct _longobject _Py_FalseStruct = {
    PyVarObject_HEAD_INIT(&PyBool_Type, 0)
    { 0 }
};

struct _longobject _Py_TrueStruct = {
    PyVarObject_HEAD_INIT(&PyBool_Type, 1)
    { 1 }
};

PyObject_IsTrue 是一个函数

PyObject_IsTrue的参数是一个 PyObject 对象,返回值是一个 int 类型的值,1 为真,0 为假,代码如下。

可以看出 v 有可能是Py_TruePy_FalsePy_None、数字类型、映射类型、序列类型的长度,长度等于 0 为假,其它情况都是真。

c
int
PyObject_IsTrue(PyObject *v)
{
    Py_ssize_t res;
    if (v == Py_True)           // 如果 v 是 Py_True,则返回 1
        return 1;
    if (v == Py_False)          // 如果 v 是 Py_False,则返回 0
        return 0;
    if (v == Py_None)           // 如果 v 是 Py_None,则返回 0
        return 0;
    else if (Py_TYPE(v)->tp_as_number != NULL &&
             Py_TYPE(v)->tp_as_number->nb_bool != NULL)     // 如果 v 是一个数字类型,则调用 nb_bool 函数
        res = (*Py_TYPE(v)->tp_as_number->nb_bool)(v);
    else if (Py_TYPE(v)->tp_as_mapping != NULL &&
             Py_TYPE(v)->tp_as_mapping->mp_length != NULL)  // 如果 v 是一个映射类型,则调用 mp_length 函数
        res = (*Py_TYPE(v)->tp_as_mapping->mp_length)(v);
    else if (Py_TYPE(v)->tp_as_sequence != NULL &&
             Py_TYPE(v)->tp_as_sequence->sq_length != NULL) // 如果 v 是一个序列类型,则调用 sq_length 函数
        res = (*Py_TYPE(v)->tp_as_sequence->sq_length)(v);
    else
        return 1;
    /* if it is negative, it should be either -1 or -2 */
    return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int);
}

全等和不全等,==和!=

全等和不全等对应的指令是IS_OP,==和!=对应的指令是COMPARE_OPIS_OP调用了Py_IsCOMPARE_OP调用了PyObject_RichCompareBool。实现原理有出入。

python
>>> a = True
>>> a is True
True
>>> a = 1
>>> a is True
False
>>>

用 dis 模块,输出一下is的字节码,结果如下。

bash
a = True
a is True

  1           0 LOAD_CONST               0 (True)
              2 STORE_NAME               0 (a)

  2           4 LOAD_NAME                0 (a)
              6 LOAD_CONST               0 (True)
              8 IS_OP                    0
             10 POP_TOP
             12 LOAD_CONST               1 (None)
             14 RETURN_VALUE

IS_OP字节码的实现如下。

c
    case TARGET(IS_OP): {           // check object identity
        PyObject *right = POP();    // 弹出栈顶的对象
        PyObject *left = TOP();     // 取出栈顶的对象
        int res = Py_Is(left, right) ^ oparg;   // 判断两个对象的内存地址是否相等
        PyObject *b = res ? Py_True : Py_False; // 如果相等则返回 Py_True,否则返回 Py_False
        Py_INCREF(b);
        SET_TOP(b);                 // 将结果压入栈顶
        Py_DECREF(left);            // 释放 left 和 right 的引用
        Py_DECREF(right);           // 释放 left 和 right 的引用
        PREDICT(POP_JUMP_IF_FALSE); // 预测下一条指令,是否是 POP_JUMP_IF_FALSE
        PREDICT(POP_JUMP_IF_TRUE);  // 预测下一条指令,是否是 POP_JUMP_IF_TRUE
        DISPATCH();
    }

==和!=的实现略微复杂

c
/* Perform a rich comparison, raising TypeError when the requested comparison
   operator is not supported. */
static PyObject *
do_richcompare(PyThreadState *tstate, PyObject *v, PyObject *w, int op)
{
    richcmpfunc f;
    PyObject *res;
    int checked_reverse_op = 0;

    if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
        PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
        (f = Py_TYPE(w)->tp_richcompare) != NULL) {     // 如果 w 是 v 的子类,则调用 w 的 tp_richcompare 函数
        checked_reverse_op = 1;
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {   // 调用 v 的 tp_richcompare 函数
        res = (*f)(v, w, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {   // 调用 w 的 tp_richcompare 函数
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    /* If neither object implements it, provide a sensible default
       for == and !=, but raise an exception for ordering. */
    switch (op) {
    case Py_EQ:
        res = (v == w) ? Py_True : Py_False;
        break;
    case Py_NE:
        res = (v != w) ? Py_True : Py_False;
        break;
    default:
        _PyErr_Format(tstate, PyExc_TypeError,
                      "'%s' not supported between instances of '%.100s' and '%.100s'",
                      opstrings[op],
                      Py_TYPE(v)->tp_name,
                      Py_TYPE(w)->tp_name);
        return NULL;
    }
    Py_INCREF(res);
    return res;
}

总结

  1. Python 中的 True 和 False 是单例模式,即全局只有一个实例。
  2. if 是对条件结果值的判断,值如果是 False、None、0、空字符串、空列表、空字典、空元组、空集合,则判断为 False,否则为 True。在弱类型语言使用 if 判断时,需要慎重考虑,是直接使用值,还是需要判断强制判断。
  3. is 和==的区别,is 是判断两个对象的内存地址是否相等,==是判断两个对象的值是否相等。