Sunteți pe pagina 1din 25

Universitatea Spiru Haret

Facultatea de Inginerie si Informatica

Fundamentele Programarii
PYTHON

Lect. univ.dr. Stancu Ana-Maria Ramona


ctypes este o bibliotecă de funcții străine pentru Python.

Oferă tipuri de date compatibile C și permite funcții de apelare în DLL-uri sau


biblioteci partajate.

Poate fi folosit pentru a înfășura aceste biblioteci în Python pur.

Observatie: Exemplele de cod folosesc doctest – ne asigura că funcționează


efectiv.
Deoarece unele exemple de cod se comportă diferit în conformitate cu Linux,
Windows sau Mac OS X, acestea conțin directive doctest în comentarii.

Observatie: Unele exemple de cod fac referire la tipul ctypes c_int.


Pe platformele unde sizeof (long) == sizeof (int) este un alias pentru c_long.
Deci, nu trebuie să vă confundați dacă c_long este tipărit dacă se asteapta ca
c_int - acestea sunt de fapt de același tip.
ctypes exportă cdll și obiectele : windll si oledll - pentru încărcarea
bibliotecilor de legături dinamice.

CDll încarcă biblioteci care exportă funcții folosind convenția de apelare


cdecl standard, în timp ce bibliotecile Windll apelează funcții folosind
convenția de apelare stdcall. oledll folosește de asemenea convenția de
apelare stdcall și presupune că funcțiile returnează un cod de eroare Windows
HRESULT. Codul de eroare este folosit pentru a ridica automat o excepție
OSError atunci când apelul funcțional eșuează.

Observatie: msvcrt este biblioteca C standard MS care conține cele mai multe
funcții C standard și folosește convenția de apelare cdecl

from ctypes import *

print (windll.kernel32)
from ctypes import *
print(cdll.msvcrt)

Accesarea bibliotecii C standard prin cdll.msvcrt va folosi o versiune învechită


a bibliotecii care poate fi incompatibilă cu cea folosită de Python. Atunci când
este posibil, utilizați funcționalitatea Python nativă, sau importați și utilizați
modulul msvcrt.

Exemple:
1) cdll.LoadLibrary("libc.so.6")

2) libc = CDLL("libc.so.6")
Accesarea funcțiilor din fișierele încărcate

Funcțiile sunt accesate ca atribute ale obiectelor dll:

from ctypes import *

libc.printf

Uneori, fișierele exportă funcții cu nume care nu sunt identificatori


Python. În acest caz, trebuie să utilizați getattr () pentru a prelua funcția:

getattr (cdll.msvcrt, "??2@YAPAXI@Z")


Pe Windows, unele dlls exportă funcții nu pe nume, ci prin ordinal. Aceste
funcții pot fi accesate prin indexarea obiectului dll cu numărul ordinal:

cdll.kernel32[1]

Funcții de apelare

Puteți apela aceste funcții ca orice alt Python apelabil. Acest exemplu folosește
funcția time (), care returnează timpul sistemului în câteva secunde de la epoca
Unix, și funcția GetModuleHandleA (), care returnează un “mâner” al
modulului win32.

Acest exemplu apelează la ambele funcții cu un indicator NULL:

print(libc.time(None))
Observtie: ValueError este ridicat atunci când apelați o funcție stdcall cu
convenția de apelare cdecl sau invers

cdll.kernel32.GetModuleHandleA(None)

Pentru a afla convenția de apelare corectă, trebuie să consultați fișierul cu


antetul C sau documentația pentru funcția pe care doriți să o apelați.

Pe Windows, ctypes folosește gestionarea excepțiilor structurate win32


pentru a preveni blocarea de la “defecțiuni generale” de protecție atunci când
funcțiile sunt apelate cu valori de argument nevalide:

windll.kernel32.GetModuleHandleA(32)
Constructorul acceptă orice obiect cu valoare de adevăr.

Toate aceste tipuri pot fi create prin apelarea lor cu un inițializator opțional
de tipul și valoarea corectă:

c_int()
c_long(0)

Deoarece aceste tipuri sunt mutabile, valoarea lor poate fi modificată și


după aceea:

i = c_int(42)
print(i)
Alocarea unei noi valori instanțelor tipurilor de pointer c_char_p, c_wchar_p
și c_void_p schimbă locația de memorie către care indică, nu conținutul
blocului de memorie

s = "Hello, World"
c_s = c_wchar_p(s)
print (c_s)

Dacă aveți nevoie de blocuri de memorie mutabile, ctypes are o funcție


create_string_buffer () care le creează în diverse moduri.

from ctypes import *


p = create_string_buffer(3)
print(sizeof(p), repr(p.raw))
printf = libc.printf
printf(b"Hello, %s\n", b"World!")

De asemenea, puteți personaliza conversia argumentelor ctypes pentru a


permite ca instanțele din propriile clase să fie utilizate ca argumente
funcționale. ctypes caută un atribut _as_parameter_ și îl folosește ca
argument al funcției. Desigur, trebuie să fie unul cu numere întregi, șiruri
sau octeți:

class Bottles:
def __init__(self, number):
self._as_parameter_ = number
bottles = Bottles(42)
printf(b"%d bottles of beer\n", bottles)
Specificarea tipurilor de argumente necesare (prototipuri de funcții)

Este posibil să specificați tipurile de argumente necesare de funcții exportate


din DLL-uri prin setarea atributului argtypes.

tipurile de date trebuie să fie o secvență de tipuri de date C

printf.argtypes = [c_char_p, c_char_p, c_int, c_double]


printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)

Specificarea unui format protejează împotriva tipurilor de argumente


incompatibile și încearcă să transforme argumentele în tipuri valide:

printf(b"%d %d %d", 1, 2, 3)
Tipuri de date -returnate

În mod implicit, se presupune că funcțiile returnează tipul C int. Alte tipuri de


retur pot fi specificate prin setarea atributului de restabilire a obiectului
funcției.

Iată un exemplu mai avansat, folosește funcția strchr, care se așteaptă la un


pointer string și un char și returnează un pointer la un șir:

strchr = libc.strchr
strchr(b"abcdef", ord("d"))

Dacă doriți să evitați apelurile ord ("x") de mai sus, puteți seta atributul
argtypes, iar cel de-al doilea argument va fi convertit dintr-un singur
caracter de bytes Python în caractere C char:
strchr.restype = c_char_p
strchr.argtypes = [c_char_p, c_char]
strchr(b"abcdef", b"d")

Trecerea indicatoarelor (sau: trecerea parametrilor prin referință)

Uneori, o funcție C api se așteaptă ca un indicator la un tip de date ca


parametru, probabil să scrie în locația corespunzătoare sau dacă datele sunt
prea mari pentru a fi trecute prin valoare. Acest lucru este, de asemenea,
cunoscut sub numele de trecere a parametrilor prin referință.

ctypes exportă funcția byref () care este utilizată pentru a trece parametrii prin
referință. Același efect poate fi obținut cu funcția pointer (), deși pointer ()
face mult mai multă muncă deoarece construiește un obiect pointer real, deci
este mai rapid să îl utilizați byref () dacă nu aveți nevoie de obiectul pointer în
Python în sine:
i = c_int()
f = c_float()
s = create_string_buffer(b'\000' * 32)
print(i.value, f.value, repr(s.value))

Structuri și uniuni

Structurile și uniunile trebuie să provină din clasele de bază Structura și


Uniunea definite în modulul ctypes. Fiecare subclasă trebuie să definească
un atribut _fields_. _fields_ trebuie să fie o listă cu 2 tupluri, care să
conțină un nume de câmp și un tip de câmp.

Tipul de câmp trebuie să fie un tip de tipuri cum ar fi c_int, sau orice alt tip
derivat de tipuri: structură, uniune, tablou, pointer.
from ctypes import *
class POINT(Structure):
_fields_ = [("x", c_int),
("y", c_int)]
point = POINT(10, 20)
print(point.x, point.y)

class RECT(Structure):
_fields_ = [("upperleft", POINT), ("lowerright", POINT)]
rc = RECT(point)
print(rc.upperleft.x, rc.upperleft.y)
Structurile cuibate pot fi, de asemenea, inițializate în constructor în mai
multe moduri

r = RECT(POINT(1, 2), POINT(3, 4))


r = RECT((1, 2), (3, 4))

Descriptorii de câmp pot fi preluați din clasă, sunt utili pentru depanare,
deoarece pot oferi informații utile:

print(POINT.x)

Observatie: ctypes nu acceptă trecerea de uniuni sau structuri cu câmpuri de


biți către funcții după valoare. Deși acest lucru poate funcționa pe x86 pe 32
de biți, biblioteca nu este garantată să funcționeze în cazul general. Uniunile
și structurile cu câmpuri de biți ar trebui să fie întotdeauna trecute la funcție
de indicatorul.
Aliniere structură / uniune și ordine de octeți

În mod implicit, câmpurile Structura și Uniunea sunt aliniate în același mod în


care îl face compilatorul C. Este posibil să treceți peste acest comportament
specificând un atribut de clasă _pack_ în definiția subclasei. Aceasta trebuie
setată la un număr întreg pozitiv și specifică alinierea maximă pentru câmpuri.

ctypes utilizează comanda de octeți nativă pentru structuri și uniuni. Pentru a


construi structuri cu ordin de octeți nativ, puteți utiliza una dintre clasele de
bază BigEndianStructure, LittleEndianStructure, BigEndianUnion și
LittleEndianUnion. Aceste clase nu pot conține câmpuri indicatoare.
Câmpuri de biți în structuri și uniuni

Este posibil să se creeze structuri și uniuni care conțin câmpuri de biți.


Câmpurile de biți sunt posibile numai pentru câmpurile întregi, lățimea de biți
este specificată ca al treilea articol din _fields_ tuples:

class Int(Structure):
_fields_ = [("first_16", c_int, 16), ("second_16", c_int, 16)]
print(Int.first_16)
Arrays

Schițele sunt secvențe, care conțin un număr fix de instanțe de același tip.

Modul recomandat de a crea tipuri de matrice este prin înmulțirea unui tip
de date cu un număr întreg pozitiv:

TenPointsArrayType = POINT * 10

Pointeri

Instanțele pointer sunt create prin apelarea funcției pointer () pe un tip ctypes:

from ctypes import *


i = c_int(42)
pi = pointer(i)
Instanțele pointer au un atribut „conținut” care returnează obiectul la care
indică indicatorul, obiectul „i”

pi.contents

Rețineți că tipurile nu au OOR (returnare obiect original), ci construiește un


obiect nou, echivalent de fiecare dată când recuperați un atribut:

pi.contents is I
Raspuns: False

pi.contents is pi.contents
Raspuns: False
Alocarea unei alte instanțe c_int la atributul conținutului pointerului ar
determina indicatorul să indice locația de memorie în care este stocată:

i = c_int(99)
pi.contents = I
pi.contents

Alocarea unui index întreg modifică valoarea indicată:

print(i)
Raspuns : c_long(99)
pi[0] = 22
print(i)
Raspuns : c_long(22)
conversiile

De obicei, tipurile efectuează verificări stricte de tip. Aceasta înseamnă că, dacă
aveți POINTER (c_int) în lista argtypes a unei funcții sau ca tip de câmp membru
într-o definiție a structurii, sunt acceptate doar cazuri de același tip. Există câteva
excepții de la această regulă, în cazul în care tipurile acceptă alte obiecte.

class Bar(Structure):
_fields_ = [("count", c_int), ("values", POINTER(c_int))]
bar = Bar()
bar.values = (c_int * 3)(1, 2, 3)
bar.count = 3
for i in range(bar.count):
print(bar.values[i])
Raspuns:
1
2
3
Tipurile incomplete sunt structuri, uniuni sau tablouri ai căror membri nu sunt
încă specificați. În C, acestea sunt specificate prin declarații forward, care sunt
definite ulterior:

struct cell; /* forward declaration */

struct cell {
char *name;
struct cell *next;
};
Funcții callback

ctypes permite crearea indicatoarelor funcției C callable din Python callables.


Acestea sunt uneori numite funcții de apelare inversă.

În primul rând, trebuie să creați o clasă pentru funcția de apelare inversă. Clasa
cunoaște convenția de apelare, tipul de retur și numărul și tipurile de argumente
pe care le va primi această funcție.

Funcția din fabrică CFUNCTYPE () creează tipuri pentru funcții de apelare


folosind convenția de apelare cdecl. Pe Windows, funcția din fabrică
WINFUNCTYPE () creează tipuri pentru funcții de apelare folosind convenția de
apelare stdcall.

Ambele funcții din fabrică sunt apelate cu tipul de rezultat ca prim argument, iar
funcțiile de retragere funcționează tipurile de argumente preconizate ca
argumente rămase.
IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
qsort = libc.qsort
qsort.restype = None

S-ar putea să vă placă și