如何将 Python boolean object 转换为 C int(或 C++ boolean)(Python C API)

How can I convert Python boolean object to C int (or C++ boolean) (Python C API)

我有一个变量 PyObject,我知道它是一个 Python 布尔值。它可以是 TrueFalse(例如 Py_TruePy_False)。现在我想以某种方式将它转换为 C++。

用字符串做这个并不难,有一个辅助函数 - PyBytes_AsString 将 python 字符串转换为 C 字符串。现在我需要类似布尔值的东西(或 int 因为 C 中没有 bool)。

或者如果没有转换,也许一些函数可以与真或假进行比较?类似于 int PyBool_IsTrue(PyObject*)?

下面是一些示例代码,可以更容易地理解我需要什么:

#include <Python.h>

int main()
{
    /* here I create Python boolean with value of True */
    PyObject *b = Py_RETURN_TRUE;
    /* now that I have it I would like to turn in into C type so that I can determine if it's True or False */
    /* something like */
    if (PyBool_IsTrue(b))
    { /* it's true! */ }
    else
    { /* it's false */ }
    return 0;
}

这显然行不通,因为没有像 PyBool_IsTrue 这样的功能 :( 我该怎么做?

Pythonheader(boolobject.h)的片段:

/* Boolean object interface */

#ifndef Py_BOOLOBJECT_H
#define Py_BOOLOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif


PyAPI_DATA(PyTypeObject) PyBool_Type;

#define PyBool_Check(x) (Py_TYPE(x) == &PyBool_Type)

/* Py_False and Py_True are the only two bools in existence.
Don't forget to apply Py_INCREF() when returning either!!! */

/* Don't use these directly */
PyAPI_DATA(struct _longobject) _Py_FalseStruct, _Py_TrueStruct;

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

/* Macros for returning Py_True or Py_False, respectively */
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False

/* Function to return a bool from a C long */
PyAPI_FUNC(PyObject *) PyBool_FromLong(long);

#ifdef __cplusplus
}
#endif
#endif /* !Py_BOOLOBJECT_H */

答案在 Python headers 中,但可能并不明显。

Python headers 在这里声明 2 个有点静态的 objects,有几个宏:

/* Don't use these directly */
PyAPI_DATA(struct _longobject) _Py_FalseStruct, _Py_TrueStruct;

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

/* Macros for returning Py_True or Py_False, respectively */
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False

似乎 TrueFalse 实际上都是 Python object 并且 Python 中的所有值都是 TrueFalse 实际上是对这两个全局 Py_TruePy_False object 的引用。当使用 Py_RETURN_TRUE 返回这样的 object 时,引用计数会增加。

这意味着每个指向值为 Py_True 的 PyObject 的 C 指针实际上都指向相同的内存地址。因此,检查 PyObject 是真还是假非常简单:

/* here I create Python boolean with value of True */
PyObject *b = Py_True;
Py_INCREF(b);
/* now we compare the address of pointer with address of Py_True */
if (b == Py_True)
{ /* it's true! */ }
else
{ /* it's false */ }

通常最好使用 int PyBool_Check(PyObject*) 来验证所讨论的 object 是否为 Python 布尔值。

每个 Python对象都可以用PyObject_IsTrue评估其真实性,你应该优先使用它而不是直接PyTrue/PyFalse 单例检查,除非你完全确定该对象是 PyBool.

用法是:

int truthy = PyObject_IsTrue(someobj);
if (truthy == -1) return APPROPRIATEERRORRETURN;

if (truthy)
{ /* it's true! */ }
else
{ /* it's false */ }

如果您知道 someobj == Py_True 绝对是布尔值,您可以只测试它,或者使用 PyNumber_AsSsize_t 转换任何逻辑整数类型(任何实现 __index__boolint 的子类,所以它在逻辑上也是一个整数)到有符号的 size_t 值(如果 __index__ returns 一个数字不适合有符号的 size_t, 它会 return -1 异常设置).

一般不做someobj == Py_True的原因是因为它就像在Python层做if someobj is True:。如果 someobj1,或者一个非空的 str,那么当 Pythonic 代码很少关心是 True 或者False,而是 "truthiness" 和 "falsiness".

另外,这个:

PyObject *b = Py_RETURN_TRUE;

完全错误。这将增加 PyTrue 和 return 它; none 的后续代码将执行。你想要:

PyObject *b = Py_True;

对于借用的参考,添加后续:

Py_INCREF(b);

如果您打算稍后 return 它,使其成为拥有的引用(因为它是一个不会消失的单例,使用借来的引用很好,除非您知道它会 DECREF 稍后编辑,例如因为您 return 编辑它并将所有权传递给不知道它是借用引用的调用者)。