上文简单介绍了python,以及在C中进行python模块的导入、函数、类接口的获得等比较基本的操作。接下来我们考虑:当我们已经获得了函数的接口之后,我们就应该能够对他进行调用了,接下来我们就来说一说函数的参数和返回值的问题。上文已经说了在python的世界里一切都是以PyObject为基类的,那么我们可以大胆的猜测,在python与C的函数接口中,入参和返回值都是PyObject*类型的。那么,问题就变成了如何将C中的简单类型转换成 PyObject*类型了,至此,我们应该去查查CPython的接口函数了。
在CPython的接口函数中,可以作为python的函数调用方式有PyAPI_FUNC(PyObject *) PyEval_CallFunction(PyObject *obj, const char *format, ...),这个函数第一个参数就是函数的Python对象,后面就是参数列表,具体的该函数调用的时候有点类似于C的printf()函数。具体可以看个例子:
PyObject *presult = PyEval_CallFunction(pFunc,"ss","5","5")
在这行代码中,pFunc是函数的PyObject对象,“ss”对应的是const char *format,后面两个是具体的参数。对应printf的话,就是printf("%s%s","5',"5")。大致就是这么个意思,他这边的参数标识是没有%做修饰的,从这里可以看出来,s对应的就是字符串,具体的对应关系比较多,本文后面会附录所有的类型对应。
现在,我们通过了PyEval_CallFunction调用了python的函数,获得了返回值,但是这个返回值是PyObject* 的,现在我们碰到的问题是,如何把这个对象转化成我们的需要的数据类型,这里Python给了一组相关的函数:
PyAPI_FUNC(char *) PyString_AsString(PyObject *);
PyAPI_FUNC(long) PyInt_AsLong(PyObject *);
PyAPI_FUNC(unsigned long) PyInt_AsUnsignedLongMask(PyObject *);
通过这些函数,我们就能够把这些Python对象转换成我们需要的数据类型了。
下面就用一个大数相加的例子,来展现python和C混合编程的魔力吧。
add.py:
def add(x,y,base,outbase):
a = int(x,base) + int(y,base)
if (outbase == 8):
return str(oct(a))
elif (outbase == 10):
return str(a)
elif (outbase == 16):
return str(hex(a))
else:
return None
cpp:
#include "stdafx.h"
#include <Python.h>
int _tmain(int argc, _TCHAR* argv[])
{
Py_Initialize();
if ( !Py_IsInitialized() )
{
return -1;
}
PyRun_SimpleString("import add");
PyObject *pName,*pMoudle,*pDict,*pFunc;
pName = PyString_FromString("add");
pMoudle = PyImport_Import(pName);
if (!pMoudle)
{
printf("get moudle handle error");
return -1;
}
pDict = PyModule_GetDict(pMoudle);
if ( !pDict )
{
printf("get moudledict handle error");
return -1;
}
pFunc = PyDict_GetItemString(pDict,"add");
if ( !pFunc || !PyCallable_Check(pFunc) )
{
printf("can't find function [add]");
getchar();
return -1;
}
PyObject *presult = PyEval_CallFunction(pFunc,"ssii","12345678ABCDEF123456789","ABCDEF12345678ABCDEF12345678",16,10);
char *pout = PyString_AsString(presult);
printf(pout);
system("pause");
return 0;
}
各位见到了吧:在C中很复杂的大数相加,在python和C的混合编程下,是不是变得异常简单了呢,当然,大数相加可以这么算,所有的大数运算都可以这么实现,大家可以去尝试一下。
以后碰到一些在C中需要写很复杂的逻辑的,但是在python中能有很好的解决方案,大家都可以尝试去这么实现,相信会给你带来不一样的编程体验。
附录
类型的转换标识:
"s" (string or Unicode object) [char *]
Convert a Python string or Unicode object to a C pointer to a character string. You must not provide storage for the string itself; a pointer to an existing string is stored into the character pointer variable whose address you pass. The C string is null-terminated. The Python string must not contain embedded null bytes; if it does, a TypeError exception is raised. Unicode objects are converted to C strings using the default encoding. If this conversion fails, an UnicodeError is raised.
"s#" (string, Unicode or any read buffer compatible object) [char *, int]
This variant on "s" stores into two C variables, the first one a pointer to a character string, the second one its length. In this case the Python string may contain embedded null bytes. Unicode objects pass back a pointer to the default encoded string version of the object if such a conversion is possible. All other read buffer compatible objects pass back a reference to the raw internal data representation.
"z" (string or None) [char *]
Like "s", but the Python object may also be None, in which case the C pointer is set to NULL.
"z#" (string or None or any read buffer compatible object) [char *, int]
This is to "s#" as "z" is to "s".
"u" (Unicode object) [Py_UNICODE *]
Convert a Python Unicode object to a C pointer to a null-terminated buffer of 16-bit Unicode (UTF-16) data. As with "s", there is no need to provide storage for the Unicode data buffer; a pointer to the existing Unicode data is stored into the Py_UNICODE pointer variable whose address you pass.
"u#" (Unicode object) [Py_UNICODE *, int]
This variant on "u" stores into two C variables, the first one a pointer to a Unicode data buffer, the second one its length.
"es" (string, Unicode object or character buffer compatible object) [const char *encoding, char **buffer]
This variant on "s" is used for encoding Unicode and objects convertible to Unicode into a character buffer. It only works for encoded data without embedded NULL bytes.
The variant reads one C variable and stores into two C variables, the first one a pointer to an encoding name string (encoding), the second a pointer to a pointer to a character buffer (**buffer, the buffer used for storing the encoded data) and the third one a pointer to an integer (*buffer_length, the buffer length).
The encoding name must map to a registered codec. If set to NULL, the default encoding is used.
PyArg_ParseTuple() will allocate a buffer of the needed size using PyMem_NEW(), copy the encoded data into this buffer and adjust *buffer to reference the newly allocated storage. The caller is responsible for calling PyMem_Free() to free the allocated buffer after usage.
"es#" (string, Unicode object or character buffer compatible object) [const char *encoding, char **buffer, int *buffer_length]
This variant on "s#" is used for encoding Unicode and objects convertible to Unicode into a character buffer. It reads one C variable and stores into two C variables, the first one a pointer to an encoding name string (encoding), the second a pointer to a pointer to a character buffer (**buffer, the buffer used for storing the encoded data) and the third one a pointer to an integer (*buffer_length, the buffer length).
The encoding name must map to a registered codec. If set to NULL, the default encoding is used.
There are two modes of operation:
If *buffer points a NULL pointer, PyArg_ParseTuple() will allocate a buffer of the needed size using PyMem_NEW(), copy the encoded data into this buffer and adjust *buffer to reference the newly allocated storage. The caller is responsible for calling PyMem_Free() to free the allocated buffer after usage.
If *buffer points to a non-NULL pointer (an already allocated buffer), PyArg_ParseTuple() will use this location as buffer and interpret *buffer_length as buffer size. It will then copy the encoded data into the buffer and 0-terminate it. Buffer overflow is signalled with an exception.
In both cases, *buffer_length is set to the length of the encoded data without the trailing 0-byte.
"b" (integer) [char]
Convert a Python integer to a tiny int, stored in a C char.
"h" (integer) [short int]
Convert a Python integer to a C short int.
"i" (integer) [int]
Convert a Python integer to a plain C int.
"l" (integer) [long int]
Convert a Python integer to a C long int.
"c" (string of length 1) [char]
Convert a Python character, represented as a string of length 1, to a C char.
"f" (float) [float]
Convert a Python floating point number to a C float.
"d" (float) [double]
Convert a Python floating point number to a C double.
"D" (complex) [Py_complex]
Convert a Python complex number to a C Py_complex structure.
"O" (object) [PyObject *]
Store a Python object (without any conversion) in a C object pointer. The C program thus receives the actual object that was passed. The object's reference count is not increased. The pointer stored is not NULL.
"O!" (object) [typeobject, PyObject *]
Store a Python object in a C object pointer. This is similar to "O", but takes two C arguments: the first is the address of a Python type object, the second is the address of the C variable (of type PyObject *) into which the object pointer is stored. If the Python object does not have the required type, TypeError is raised.
"O&" (object) [converter, anything]
Convert a Python object to a C variable through a converter function. This takes two arguments: the first is a function, the second is the address of a C variable (of arbitrary type), converted to void *. The converter function in turn is called as follows:
status = converter(object, address);
where object is the Python object to be converted and address is the void * argument that was passed to PyArg_ConvertTuple(). The returned status should be 1 for a successful conversion and 0 if the conversion has failed. When the conversion fails, the converter function should raise an exception.
"S" (string) [PyStringObject *]
Like "O" but requires that the Python object is a string object. Raises TypeError if the object is not a string object. The C variable may also be declared as PyObject *.
"U" (Unicode string) [PyUnicodeObject *]
Like "O" but requires that the Python object is a Unicode object. Raises TypeError if the object is not a Unicode object. The C variable may also be declared as PyObject *.
"t#" (read-only character buffer) [char *, int]
Like "s#", but accepts any object which implements the read-only buffer interface. The char * variable is set to point to the first byte of the buffer, and the int is set to the length of the buffer. Only single-segment buffer objects are accepted; TypeError is raised for all others.
"w" (read-write character buffer) [char *]
Similar to "s", but accepts any object which implements the read-write buffer interface. The caller must determine the length of the buffer by other means, or use "w#" instead. Only single-segment buffer objects are accepted; TypeError is raised for all others.
"w#" (read-write character buffer) [char *, int]
Like "s#", but accepts any object which implements the read-write buffer interface. The char * variable is set to point to the first byte of the buffer, and the int is set to the length of the buffer. Only single-segment buffer objects are accepted; TypeError is raised for all others.
"(items)" (tuple) [matching-items]
The object must be a Python sequence whose length is the number of format units in items. The C arguments must correspond to the individual format units in items. Format units for sequences may be nested.
Note: Prior to Python version 1.5.2, this format specifier only accepted a tuple containing the individual parameters, not an arbitrary sequence. Code which previously caused TypeError to be raised here may now proceed without an exception. This is not expected to be a problem for existing code.
It is possible to pass Python long integers where integers are requested; however no proper range checking is done -- the most significant bits are silently truncated when the receiving field is too small to receive the value (actually, the semantics are inherited from downcasts in C -- your mileage may vary).
A few other characters have a meaning in a format string. These may not occur inside nested parentheses. They are:
"|"
Indicates that the remaining arguments in the Python argument list are optional. The C variables corresponding to optional arguments should be initialized to their default value -- when an optional argument is not specified, PyArg_ParseTuple() does not touch the contents of the corresponding C variable(s).
":"
The list of format units ends here; the string after the colon is used as the function name in error messages (the ``associated value'' of the exception that PyArg_ParseTuple() raises).
";"
The list of format units ends here; the string after the colon is used as the error message instead of the default error message. Clearly, ":" and ";" mutually exclude each other.