#python #c #python-c-api
#python #c #python-c-api
Вопрос:
У меня есть переменная PyObject
, которая, как я знаю, является Python bool . Это либо True
или False
(например. Py_True
или Py_False
). Теперь я хотел бы каким-то образом преобразовать его в C .
Сделать это со строками не так сложно, для этого есть вспомогательная функция, PyBytes_AsString
которая преобразует строку python в строку C. Теперь мне нужно что-то подобное для boolean (или int, поскольку bool
в C его нет).
Или, если преобразования нет, может быть, какая-то функция, которая может сравниваться с true или false? Что-то вроде 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) == amp;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 *) amp;_Py_FalseStruct)
#define Py_True ((PyObject *) amp;_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 */
Ответ №1:
Для каждого объекта Python может быть оценена его PyObject_IsTrue
истинность, и вы должны использовать это предпочтительнее прямой PyTrue
/ PyFalse
одноэлементной проверки, если вы абсолютно точно не знаете, что объект является a PyBool
.
Использование:
int truthy = PyObject_IsTrue(someobj);
if (truthy == -1) return APPROPRIATEERRORRETURN;
if (truthy)
{ /* it's true! */ }
else
{ /* it's false */ }
Вы можете просто проверить, someobj == Py_True
знаете ли вы, что это определенно bool, или использовать PyNumber_AsSsize_t
для преобразования любого логического целочисленного типа (любого реализующего __index__
и bool
являющегося подклассом int
, поэтому он также логически является целым числом) size_t
в значение со знаком (если __index__
возвращает число, которое не вписывается в signed size_t
, оно вернет -1 снабор исключений).
Причина, по которой этого не делать someobj == Py_True
вообще, заключается в том, что это похоже на выполнение if someobj is True:
на уровне Python. Если someobj
это 1
или непустое str
значение, это будет рассматривать его как false , когда Pythonic code редко заботится о том, чтобы быть True
или False
, скорее, «правдивость» и «ложность».
Кроме того, это:
PyObject *b = Py_RETURN_TRUE;
это неверно. Это увеличит PyTrue
и вернет его; ни один из последующих кодов не будет выполнен. Вы бы хотели:
PyObject *b = Py_True;
для заимствованной ссылки добавьте следующий:
Py_INCREF(b);
чтобы сделать его принадлежащей ссылкой, если вы намеревались вернуть ее позже (поскольку это синглтон, который никуда не денется, использование заимствованной ссылки подойдет, если вы не знаете, что она будет DECREF
отредактирована позже, например, потому что вы вернули ее и передали право собственности вызывающему, который не может знать, что это заимствованная ссылка).
Комментарии:
1. Будет
PyObject *b = Py_RETURN_TRUE;
ли даже компилироваться?2. @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 *) amp;_Py_FalseStruct)
#define Py_True ((PyObject *) amp;_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
Кажется, что оба True
и False
на самом деле являются объектами Python и всеми значениями в Python, которые являются True
или False
фактически являются ссылками на эти два глобальных Py_True
Py_False
объекта and . Когда такой объект возвращается с помощью Py_RETURN_TRUE
, количество ссылок увеличивается.
Это означает, что каждый указатель C, который указывает на PyObject значения Py_True
, фактически указывает на тот же адрес памяти. Поэтому проверка истинности 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.
Комментарии:
1. Не очень хороший совет и не решает некоторые основные проблемы в подходе OP.