【问题标题】:How can I convert Python boolean object to C int (or C++ boolean) (Python C API)如何将 Python 布尔对象转换为 C int(或 C++ 布尔值)(Python C API)
【发布时间】:2016-10-09 21:00:18
【问题描述】:

我有一个变量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 这样的功能 :( 我该怎么做?

Python 标头 (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 c python-c-api


【解决方案1】:

每个 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__ 返回的数字不适合带符号的size_t,它将返回-1 并设置异常)。

一般不做someobj == Py_True的原因是因为它就像在Python层做if someobj is True:。如果someobj1 或非空str,则将其视为错误,而Pythonic 代码很少关注TrueFalse,而是“真实性”和“虚假性” ”。

另外,这个:

PyObject *b = Py_RETURN_TRUE;

完全错误。这将增加 PyTrue 并返回它;后续代码都不会执行。你想要:

PyObject *b = Py_True;

对于借用的引用,添加后续:

Py_INCREF(b);

如果您打算稍后将其归还,则使其成为拥有的引用(因为它是一个不会消失的单例,所以使用借用的引用很好,除非您知道稍后它将是 DECREFed,例如因为您返回并将所有权传递给不知道它是借用引用的调用者)。

【讨论】:

  • PyObject *b = Py_RETURN_TRUE; 会编译吗?
  • @MadPhysicist:可能不会,但我没有放过一个在非严格模式下运行并关闭警告的编译器。 :-)
【解决方案2】:

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

Python 头文件在这里声明了 2 个有点静态的对象,带有几个宏:

/* 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 对象,而Python 中所有TrueFalse 的值实际上都是对这两个全局Py_TruePy_False 对象的引用。当使用Py_RETURN_TRUE 返回此类对象时,引用计数会增加。

这意味着每个指向 PyObject 值 Py_True 的 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*) 来验证相关对象是否为 Python 布尔值通常是个好主意。

【讨论】:

  • 不是很好的建议,也没有解决 OP 方法中的一些主要问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
  • 1970-01-01
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 2012-02-25
相关资源
最近更新 更多