没有合适的资源?快使用搜索试试~ 我知道了~
Python 扩展C C++, Python中直接调用C C++写的底层代码
资源推荐
资源详情
资源评论
2017/10/11 1.ExtendingPythonwithCorC++—Python3.6.3documentation
https://docs.python.org/3/extending/extending.html 1/23
1.ExtendingPythonwithCorC++
Itis quiteeasytoaddnewbuiltinmodulestoPython,ifyouknowhowtoprograminC.Such
extension modules can do two things that can’t be done directly in Python: they can
implementnewbuiltinobjecttypes,andtheycancallClibraryfunctionsandsystemcalls.
Tosupportextensions,thePythonAPI(ApplicationProgrammersInterface)definesasetof
functions,macrosandvariablesthatprovideaccesstomostaspectsofthePythonruntime
system.ThePythonAPIisincorporatedinaCsourcefilebyincludingtheheader"Python.h".
The compilation of an extension module depends on its intended use as well as on your
systemsetup;detailsaregiveninlaterchapters.
Note: TheCextensioninterfaceisspecifictoCPython,andextensionmodulesdonot
workonotherPythonimplementations.Inmanycases,itispossibletoavoidwritingC
extensionsandpreserveportabilitytootherimplementations.Forexample,ifyourusecase
iscallingClibraryfunctionsorsystemcalls,youshouldconsiderusingthectypesmodule
orthecffilibraryratherthanwritingcustomCcode.ThesemodulesletyouwritePython
codetointerfacewithCcodeandaremoreportablebetweenimplementationsofPython
thanwritingandcompilingaCextensionmodule.
1.1.ASimpleExample
Let’screateanextensionmodulecalled spam(thefavoritefoodofMontyPythonfans…)and
let’s say we want to create a Python interface to the C library function system(). [1] This
function takesanullterminatedcharacterstringasargumentandreturnsaninteger.Wewant
thisfunctiontobecallablefromPythonasfollows:
>>> import spam
>>> status = spam.system("ls -l")
Begin by creating a file spammodule.c. (Historically, if a module is called spam, the C file
containing its implementation is called spammodule.c; if the module name is very long, like
spammify,themodulenamecanbejustspammify.c.)
Thefirstlineofourfilecanbe:
#include
<Python.h>
whichpullsinthePythonAPI(youcanaddacommentdescribingthepurposeofthemodule
andacopyrightnoticeifyoulike).
Note: SincePythonmaydefinesomepreprocessordefinitionswhichaffectthestandard
headersonsomesystems,youmustincludePython.hbeforeanystandardheadersare
included.
2017/10/11 1.ExtendingPythonwithCorC++—Python3.6.3documentation
https://docs.python.org/3/extending/extending.html 2/23
AlluservisiblesymbolsdefinedbyPython.hhaveaprefixofPyorPY,exceptthosedefinedin
standardheader files.For convenience,andsince theyare usedextensivelyby the Python
interpreter, "Python.h" includes a few standard header files: <stdio.h>, <string.h>,
<errno.h>,and<stdlib.h>.Ifthelatterheaderfiledoesnotexistonyoursystem,itdeclares
thefunctionsmalloc(),free()andrealloc()directly.
ThenextthingweaddtoourmodulefileistheCfunctionthatwillbecalledwhenthePython
expressionspam.system(string)isevaluated(we’llseeshortlyhowitendsupbeingcalled):
static
PyObject *
spam_system(PyObject *self, PyObject *args)
{
const
char *command;
int sts;
if
(!PyArg_ParseTuple(args, "s", &command))
return
NULL;
sts = system(command);
return
PyLong_FromLong(sts);
}
ThereisastraightforwardtranslationfromtheargumentlistinPython(forexample,thesingle
expression "ls -l")totheargumentspassedtotheCfunction.TheC function alwayshas
twoarguments,conventionallynamedselfandargs.
The self argument points to the module object for modulelevel functions; for a method it
wouldpointtotheobjectinstance.
TheargsargumentwillbeapointertoaPythontupleobjectcontainingthearguments.Each
itemofthetuplecorrespondstoanargumentinthecall’sargumentlist.The arguments are
Pythonobjects—inordertodo anythingwiththeminourCfunctionwehavetoconvertthem
toCvalues.Thefunction PyArg_ParseTuple()inthePythonAPIcheckstheargumenttypes
andconvertsthemtoCvalues.Itusesatemplatestringtodeterminetherequiredtypesofthe
argumentsaswellasthetypesoftheCvariablesinto which to store the converted values.
Moreaboutthislater.
PyArg_ParseTuple() returns true (nonzero) if all arguments have the right type and its
componentshavebeenstoredinthevariableswhoseaddressesarepassed.Itreturnsfalse
(zero)ifan invalid argumentlistwaspassed.Inthelattercase it alsoraisesanappropriate
exceptionsothecallingfunctioncanreturnNULLimmediately(aswesawintheexample).
1.2.Intermezzo:ErrorsandExceptions
An important conventionthroughout thePython interpreteris thefollowing: when afunction
fails,itshouldsetanexceptionconditionandreturnanerrorvalue(usuallyaNULLpointer).
Exceptionsarestoredinastaticglobalvariableinsidetheinterpreter;ifthisvariableisNULL
no exception has occurred. A second global variable stores the “associated value” of the
exception (the second argument to raise). A third variable contains the stack traceback in
casetheerrororiginatedinPythoncode.ThesethreevariablesaretheCequivalentsofthe
2017/10/11 1.ExtendingPythonwithCorC++—Python3.6.3documentation
https://docs.python.org/3/extending/extending.html 3/23
result in Python of sys.exc_info() (see the section on module sys in the Python Library
Reference).Itisimportanttoknowaboutthemtounderstandhowerrorsarepassedaround.
ThePythonAPIdefinesanumberoffunctionstosetvarioustypesofexceptions.
Themostcommononeis PyErr_SetString().ItsargumentsareanexceptionobjectandaC
string.Theexceptionobjectisusuallyapredefinedobjectlike PyExc_ZeroDivisionError.The
CstringindicatesthecauseoftheerrorandisconvertedtoaPythonstringobjectandstored
asthe“associatedvalue”oftheexception.
AnotherusefulfunctionisPyErr_SetFromErrno(),whichonlytakesanexceptionargumentand
constructstheassociatedvaluebyinspectionoftheglobalvariable errno.Themostgeneral
function is PyErr_SetObject(), which takes two object arguments, the exception and its
associatedvalue.Youdon’tneedtoPy_INCREF()theobjectspassedtoanyofthesefunctions.
You can test nondestructively whether an exception has been set with PyErr_Occurred().
Thisreturnsthecurrentexceptionobject,orNULLif noexceptionhasoccurred.Younormally
don’tneedtocallPyErr_Occurred()toseewhetheranerroroccurredinafunctioncall,since
youshouldbeabletotellfromthereturnvalue.
When afunctionfthatcallsanotherfunctiongdetectsthatthelatterfails,fshould itselfreturn
anerrorvalue(usuallyNULLor -1).Itshouldnotcalloneofthe PyErr_*()functions—one
hasalreadybeencalledbyg.f’scalleristhensupposedtoalsoreturnanerrorindicationtoits
caller,againwithoutcallingPyErr_*(),andsoon—themostdetailedcauseoftheerrorwas
already reported by the function that first detected it. Once the error reaches the Python
interpreter’smain loop, this aborts thecurrently executing Python code and tries to findan
exceptionhandlerspecifiedbythePythonprogrammer.
(There are situations where a module can actually give a more detailed error message by
calling another PyErr_*() function,and insuch casesit isfine todo so.As ageneral rule,
however,thisisnotnecessary,andcancauseinformationaboutthecauseoftheerrortobe
lost:mostoperationscanfailforavarietyofreasons.)
To ignore an exception set by a function call that failed, the exception condition must be
clearedexplicitlybycallingPyErr_Clear().TheonlytimeCcodeshouldcallPyErr_Clear()is
ifitdoesn’twantto passtheerrorontotheinterpreterbutwantstohandleitcompletelyby
itself(possiblybytryingsomethingelse,orpretendingnothingwentwrong).
Everyfailingmalloc()callmustbeturnedintoanexception—thedirect callerofmalloc()(or
realloc()) must call PyErr_NoMemory() and return a failure indicator itself. All the object
creating functions (for example, PyLong_FromLong()) already do this, so this note is only
relevanttothosewhocallmalloc()directly.
Alsonotethat,withtheimportantexceptionof PyArg_ParseTuple()andfriends,functionsthat
returnanintegerstatususuallyreturnapositivevalueorzeroforsuccessand -1forfailure,
likeUnixsystemcalls.
Finally, be careful to clean up garbage (by making Py_XDECREF() or Py_DECREF() calls for
objectsyouhavealreadycreated)whenyoureturnanerrorindicator!
2017/10/11 1.ExtendingPythonwithCorC++—Python3.6.3documentation
https://docs.python.org/3/extending/extending.html 4/23
The choice of which exception to raise is entirely yours. There are predeclared C objects
correspondingtoallbuiltinPythonexceptions,suchas PyExc_ZeroDivisionError,whichyou
can use directly. Of course, you should choose exceptions wisely — don’t use
PyExc_TypeError to mean that a file couldn’t be opened (that should probably be
PyExc_IOError).Ifsomething’swrongwiththeargumentlist,the PyArg_ParseTuple()function
usuallyraisesPyExc_TypeError.Ifyouhaveanargumentwhosevaluemustbeinaparticular
rangeormustsatisfyotherconditions,PyExc_ValueErrorisappropriate.
You can also define a new exception that is unique to your module. For this, you usually
declareastaticobjectvariableatthebeginningofyourfile:
static
PyObject *SpamError;
andinitializeitinyourmodule’sinitializationfunction(PyInit_spam())withanexceptionobject
(leavingouttheerrorcheckingfornow):
PyMODINIT_FUNC
PyInit_spam(void)
{
PyObject *m;
m = PyModule_Create(&spammodule);
if
(m == NULL)
return
NULL;
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Py_INCREF(SpamError);
PyModule_AddObject(m, "error", SpamError);
return
m;
}
NotethatthePythonnamefortheexceptionobjectisspam.error.The PyErr_NewException()
function may create a class with the base class being Exception (unless another class is
passedininsteadofNULL),describedinBuiltinExceptions.
NotealsothattheSpamErrorvariableretainsareferencetothenewlycreatedexceptionclass;
thisisintentional!Sincetheexceptioncouldberemovedfromthemodulebyexternalcode,
an owned reference to the class is needed to ensure that it will not be discarded, causing
SpamErrortobecomeadanglingpointer.Shoulditbecomeadanglingpointer,Ccodewhich
raisestheexceptioncouldcauseacoredumporotherunintendedsideeffects.
WediscusstheuseofPyMODINIT_FUNCasafunctionreturntypelaterinthissample.
The spam.error exception can be raised in your extension module using a call to
PyErr_SetString()asshownbelow:
static
PyObject *
spam_system(PyObject *self, PyObject *args)
{
const
char *command;
int sts;
2017/10/11 1.ExtendingPythonwithCorC++—Python3.6.3documentation
https://docs.python.org/3/extending/extending.html 5/23
if
(!PyArg_ParseTuple(args, "s", &command))
return
NULL;
sts = system(command);
if
(sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return
NULL;
}
return
PyLong_FromLong(sts);
}
1.3.BacktotheExample
Goingbacktoourexamplefunction,youshouldnowbeabletounderstandthisstatement:
if
(!PyArg_ParseTuple(args, "s", &command))
return
NULL;
It returns NULL (the error indicator for functions returning object pointers) if an error is
detectedintheargumentlist,relyingontheexceptionsetby PyArg_ParseTuple().Otherwise
the string value of the argument has been copied to the local variable command. This is a
pointer assignment and you are not supposed to modify the string to which it points (so in
StandardC,thevariablecommandshouldproperlybedeclaredasconst char *command).
The next statementis a callto the Unixfunction system(), passingit thestring wejust got
fromPyArg_ParseTuple():
sts = system(command);
Our spam.system() function must return the value of sts as a Python object. This is done
usingthefunctionPyLong_FromLong().
return
PyLong_FromLong(sts);
In this case, it will return an integer object. (Yes, even integers are objects on the heap in
Python!)
If you have a C function that returns no useful argument (a function returning void), the
corresponding Python function must return None. You need this idiom to do so (which is
implementedbythePy_RETURN_NONEmacro):
Py_INCREF(Py_None);
return
Py_None;
Py_NoneistheCnameforthespecialPythonobjectNone.ItisagenuinePythonobjectrather
thanaNULLpointer,whichmeans“error”inmostcontexts,aswehaveseen.
1.4.TheModule’sMethodTableandInitialization
Function
剩余22页未读,继续阅读
资源评论
qq_40383507
- 粉丝: 0
- 资源: 1
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功