Išplė&mas Esamo funkcionalumo papildymas naujomis galimybėmis
Kam to reikia? Realizuoti naujus įtaisytuosius (built- in) objektų tipus Iškviesti C bibliotekų funkcijas ir sisteminius kvietimus
Ko reikia? Reikalingas Python API: Funkcijos Makrokomandos Kintamieji Python.h
Paprastas pavyzdys >>> import spam >>> status = spam.system("ls - l")
#include <Python.h> // Čia turėtų būti norima C funkcija //Funkcijos API static PyObject * spam_system(pyobject *self, PyObject *args){ const char *command; int sts; if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = system(command); if (sts < 0) { //PyErr_SetString(spamError, "System command failed"); return NULL; } return Py_BuildValue("i", sts); }
Paaiškinimai (I) Vyksta tiesioginis reikšmių iš Python o į C vertimas. C funkcija visada turi du kintamuosius self ir args self naudojamas realizuojant įtaisytuosius metodus, bet ne funkcijas args yra rodyklė į Python o kortežą (tuple), saugantį argumentus, kur kiekvienas elementas atitinka argumentą vertimo funkcijos argumentų sąrašę. Vertimą atlieka funkcija PyArg_ParseTuple()
Paaiškinimai (II) PyArg_ParseTuple grąžina True, jei visi argumentai turi atitinkamą tipą ir būna išsaugoti kintamųjų, kurių adresai perduoti grąžina False, jei perduotas neteisingas argumentų sąrašas Grąžinamas NULL yra klaidos požymis!!! sts yra funkcijos kvietimo rezultatas, bet jį reikia paversti Python o objektų
Paaiškinimai (III) Py_BuildValue(tipas, reikšmė) funkcija, verčianti rezultatą į objektą. Reikia nurodyti kokio tipo reikšmė bus verčiama. Jeigu funkcija yra void tipo, tuomet ji vistiek turi grąžinti Python o atitikmenį None Py_None
Metodų lentelė static PyMethodDef spammethods[] = { {"system", spam_system, METH_VARARGS, "Execute a shell command."}, {NULL, NULL, 0, NULL} /* Sentinel */ };
Paaiškinimai 1 parametras vardas 2 parametras funkcijos API vardas 3 parametras požymis (flag); visada turėtų būti METH_VARARGS [ METH_KEYWORDS]; 0 reiškia pasenusios (obsolete) funkcijos PyArg_ParseTuple naudojimą Jei naudojami vardiniai argumentai, tuomet reikalingas METH_KEYWORDS požymis ir PyArg_ParseTupleAndKeywords() funkcija
Inicializavimas Python 2 Python 3 PyMODINIT_FUNC initspam (void){ (void) Py_InitModule ("spam", spammethods); } static struct PyModuleDef spammodule = { PyModuleDef_HEAD_INIT, "spam", // name of module NULL, // module documentation - 1, // size of per- interpreter state spammethods }; PyMODINIT_FUNC PyInit_spam (void){ return PyModule_Create (&spammodule); }
Paaiškinimai Python 2 atveju initname() yra inicializuojanti funkcija, kur name yra modulio vardas Py_InitModule(modulio_vardas, metodų_lentelė) Python 3 atveju reikia apsirašyti modulio struktūrą ir tik tada inicializuojamas modulis PyInit_name, kur name yra modulio vardas PyModule_Create(modulio_struktūra)
Kompiliavimas distutils paprastas įrankis, trūksta funkcionalumo setuptools iš esmės išplečia distutils apribojimus distribute sujungtas su setuptools distutils2 apleistas projektas distlib - vystomas? bento turėtų pakeisti ankstesnius įrankius. Vystomas?
setup.py from distutils.core import setup, Extension module1 = Extension( demo, sources = [ demo.c ]) setup (name = PackageName, version = 1.0, description = This is a demo package, ext_modules = [module1])
Pavyzdys from distutils.core import setup from distutils.core import Extension MOD = "spam" module = Extension(MOD, sources = ["spammodule.c"]) setup(name = MOD, ext_modules = [module])
Baigiant python3 setup.py build python3 setup.py install python3 setup.py install - - user
Reikšmių ver&mas į C Reikšmės verčiamos į C dviejų funkcijų pagalba: PyArg_ParseTuple(PyObject *arg, char *format,...) PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, char *format, char *kwlist[],...)
PyArg_ParseTuple(PyObject *arg, char *format,...) Kadangi verčiamas objektas, tai jo struktūra gali būti sudėtinga, bet ją galima apsirašyti *format pagalba s simbolių eilutė (ii)s# dviejų sveikųjų skaičių kortežas ir simbolių eilutė su jos ilgiu s si simbolių eilutė ir galimi papildomai antra simbolių eilutė ir sveikasis skaičius
PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, char *format, char *kwlist[],...) arg argumentai kwdict vardiniai argumentai format formatavimas kwlist vardinių argumentų vardų sąrašas int voltage; char *state; static char *kwlist[] = { voltage, state, NULL} PyArg_ParseTupleAndKeywords(args, keywds, i s, kwlist, &voltage, &state)
Reikšmių ver&mas į Python ą PyObject *Py_BuildValue(char *format,...) None i sveikas skaičius (ii) dviejų sveikųjų skaičių kortežas s# simbolių eilutė ir ją sudarančių simbolių skaičius [i,i] dviejų sveikųjų skaičių sąrašas {s:i, s:i} - žodynas
Rodyklių skaičiavimas (reference count) Metodika skirta atminties nutekėjimo (memory leak) valdymui Kiekvienas objektas turi skaitliuką, kuris padidinamas, kai yra išsaugoma rodyklė į objektą, ir sumažinamas, kai rodyklė ištrinama.
Py_INCREF, Py_DECREF Makrosai, atsakingi už objekto rodyklių skaitliukų valdymą Egzistuoja analogiški makrosai Py_XINCREF ir Py_XDECREF Patikrina NULL rodyklių egzistavimą
NULL problema Funkcijų argumentai neturėtų būti NULL Funkcijos rezultatas neturėtų būti NULL NULL suprantamas kaip įvykusi klaida Priešingu atveju reikėtų rašyti daug perteklinio kodo NULL rodyklėms suvaldyti
Klaidos ir išimtys Išimtys saugomos statinio globalaus kintamojo interpretatoriaus viduje, jei jis yra NULL, išimtis neįvykusi Antrasis globalus kintamasis saugo išimties susijusią reikšmę Trečiasis kintamasis saugo steko pėdsaką (stack traceback) Šie trys kintamieji yra C kalbos ekvivalentas Python o kintamiesiems sys.exc_type, sys.exc_value, sys.exc_traceback
Klaidų funkcijos PyErr_SetString(exception_object, string) Išimties objektas paprastai būna iš anksto apibrėžtas objektas kaip PyExc_ZeroDivisionError Simbolių eilutė aprašo klaidos priežastį PyErr_SetFromErrno(exception_argument) PyErr_SetObject(exception, value) bendriausia funkcija PyErr_Occured() leidžia patikrintiar įvyko klaida klaidos objektas arba NULL
Klaidų mechanizmas Jei funkcija f kviečia funkciją g ir pastaroji grąžino klaidą, funkcija f taip pat turėtų grąžinti klaidos reikšmę (NULL arba - 1) PyErr_* funkcijų tokiu atveju kviesti papildomai nebereikia Gali pasitaikyti atvejų, kuomet tai vis dėlto daroma tam, kad tiksliau aprašyti klaidą PyErr_Clear() išvalo klaidą jos neperduodant interpretatoriui
Nauja klaida Galima sukurti naują klaidą (Python 2 kodas): static PyObject *spamerror; initspam(void){ PyObject *m; spamerror = PyErr_NewException("spam.error", NULL, NULL); Py_INCREF(spamError); PyModule_AddObject(m, "error", spamerror); }
Nauja klaida Galima sukurti naują klaidą (Python 3 kodas): static PyObject *spamerror; PyMODINIT_FUNC PyInit_spam(void){ PyObject *m=pymodule_create(&spammodule); spamerror = PyErr_NewException("spam.error", NULL, NULL); Py_INCREF(spamError); PyModule_AddObject(m, "error", spamerror); }
Naujo &po apibrėžimas Python o interpretatorius mato visus objektus kaip PyObject PyObject saugo rodyklės skaitliuką ir rodyklę į objekto tipą ( type object ) Tipas apibrėžia kokios funkcijos (dar vadinamos tipo metodais) yra kviečiamos
Tipas typedef struct{ PyObject_HEAD /* Type-specific fields go here. */ } noddy_noddyobjec t; PyObject_HEAD makrosas, kuris užtikrina rodyklės skaitliuko ir rodyklės įtraukimą Kabliataškis nededamas!
Objektas static PyTypeObject noddy_noddytype ={ PyVarObject_HEAD_INIT(NULL, 0) "noddy.noddy", /*tp_name*/ sizeof(noddy_noddyobject), / *tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_reserved*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Noddy objects", /* tp_doc */ }; PyTypeObject C įrašo tipas, aprašantis įtaisytuosius tipus Laukų yra daugiau, tačiau paprastai jie paliekami užpildyti kompiliatoriui Kiekvienas iš šiuo metu neaprašytų laukų turi savo paskirtį, pavyzdžiui, tp_itemsize reikalingas kintamo dydžio objektams
Modulis static struct PyModuleDef noddymodule ={ PyModuleDef_HEAD_INIT, "noddy", // name of module "Example of new type", // module documentation, may be NULL -1, // size of perinterpreter state of the module, or -1 if the module keeps state in global variables. NULL, NULL, NULL, NULL, NULL}; Modulio aprašymas
PyType_GenericNew PyMODINIT_FUNC PyInit_noddy(void) { PyObject* m; noddy_noddytype.tp _new = PyType_GenericNew; if (PyType_Ready (&noddy_noddytype) < 0) return NULL; PyType_GenericNew bendroji funkcija (generic handler), sukurianti naują objektą (atminties išskyrimas) PyType_Ready() inicializuoja tipą
Užbaigiant modulį m = PyModule_Create (&noddymodule); if (m == NULL) return NULL; Py_INCREF (&noddy_noddytype); PyModule_AddObject (m, "Noddy", (PyObject *)&noddy_noddytype); return m;} PyModule_AddObject() įtraukia tipą į modulio žodyną
Suteikiant funkcionalumo Įrašo tipą galima papildyti norimais laukais Aprašomos funkcijos objektui sukurti (pvz. new, init ir t.t.) ar sunaikinti ir valdyti (pvz. set, get), taip pat metodai Užpildoma laukų lentelė PyMemberDef Užpildoma valdymo funkcijų lentelė PyGetSetDef Užpildoma metodų lentelė PyMethodDef Aprašomas objektas PyTypeObject, nurodant metodus ir lenteles Užpildomas modulio aprašas ir aprašoma modulio inicializavimo funkcija
Įterpimas Duomenų konvertavimas iš C į Python ą Kviečiama Python o sąsaja Duomenų konvertavimas iš Python o į C
#include <Python.h>! int main(int argc, char *argv[]){! Py_Initialize();! PyRun_SimpleString("from time import time,ctime\n"! "print('today is', ctime(time()))\n");! Py_Finalize();! return 0;! } Pats paprasčiausias būdas įterpti Python o kodą: Inicializuojame Python o interpretatorių Įvykdome kodą Užbaigiame darbą su interpretatorium
Sudė&ngesnis pavyzdys #include <Python.h>! int main(int argc, char *argv[]){! PyObject *pname, *pmodule, *pdict, *pfunc;! PyObject *pargs, *pvalue;! int i;! if (argc < 3) {! fprintf(stderr,"usage: call pythonfile funcname [args]\n");! return 1;! }! Standartinė pradžia. Patikriname ar pakaks parametrų.
Pradedame darbą Py_Initialize(); pname = PyString_FromString(argv[1]); PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.insert(0, '')"); pmodule = PyImport_Import(pName); Py_DECREF(pName); Pirmasis programos parametras paverčiamas objektu, tai bus Python o failo (modulio) vardas Nustatome, kad darbinis Python o katalogas būtų dabartinis katalogas, kuriame yra failas Importuojame failą kaip modulį
Judame toliau if (pmodule!= NULL) { pfunc = PyObject_GetAttrString (pmodule, argv[2]); /* pfunc is a new reference */ if (pfunc && PyCallable_Check (pfunc)) { pargs = PyTuple_New(argc - 3); Jei su moduliu viskas gerai, iš jo pasiimame funkciją, kurios vardas yra antrasis programos parametras Jei su funkcija viskas gerai, konstruojame kortežą parametrams
Parametrai, parametrai... for (i = 0; i < argc - 3; ++i) { pvalue = PyInt_FromLong(atoi(argv[i + 3])); if (!pvalue) { Py_DECREF(pArgs); Py_DECREF(pModule); fprintf(stderr, "Cannot convert argument\n"); return 1; } /* pvalue reference stolen here: */ PyTuple_SetItem(pArgs, i, pvalue); } Nuskaitome funkcijos parametrus iš programos parametrų ir juos konvertuojame į Python o tipus Jei konvertavimas pavyko, įdedame reikšmę į kortežą
Funkcijos kvie&mas pvalue = PyObject_CallObject(pFunc, pargs); Py_DECREF(pArgs); if (pvalue!= NULL) { printf("result of call: %ld\n, PyInt_AsLong(pValue)); Py_DECREF(pValue); } else { Py_DECREF(pFunc); Py_DECREF(pModule); PyErr_Print(); fprintf(stderr,"call failed\n"); return 1; } } Kviečiame funkciją jai perduodami paramtrus Jei funkcija sėkmingai įvykdyta, rezultatą spausdiname kaip C reikšmę (long) Jei blogai spausdinamas klaidos pranešimas
Pabaiga else { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]); } Py_XDECREF(pFunc); Py_DECREF(pModule); } else { PyErr_Print(); fprintf(stderr, "Failed to load \"%s \"\n", argv[1]); return 1; } Py_Finalize(); return 0; } Sutvarkome likusias klaidas
Failas Dešinėje matoma reikalinga funkcija Funkcija turėtų būti patalpinta faile multiply.py, nors tiktų bet koks pavadinimas Šis failas ir programos c kodas turėtų btūi viename kataloge def multiply(a, b): print ("Will compute", a, "times", b) c = 0 for i in range(0, a): c = c + b return c
Kompiliavimas Norint sukompiliuoti įterptinį kodą, reikia kompiliatoriui nurodyti papildomas instrukcijas Kokias žymes naudoti compiliuojant, galima sužinoti komandos python3- config cflags pagalba Kokias žymes naudojant susiejant (linking), galima sužinoti komandos python3- config ldflags pagalba Pavyzdys (MIF linux klasėse): gcc - I/usr/include/python3.4m embed.c - lpython3.4m
Pagaliau! $./a.out multiply multiply 3 2 Will compute 3 times 2 Result of call: 6 Kviečiame sukompiliuotą programą Vykdymo rezultatas