Limbajul Si Ecosistemul de Programare Python Eugen Diaconescu V1.0
Limbajul Si Ecosistemul de Programare Python Eugen Diaconescu V1.0
Limbajul Si Ecosistemul de Programare Python Eugen Diaconescu V1.0
Referenți:
Dr. ing. Florentina Magda Enescu
Dr. ing. Rizea Vasile
DIACONESCU, EUGEN
Limbajul și ecosistemul de programare Python
Editura Universității din Pitești, 2022
e-ISBN: 978-606-560-754-5
Eugen Diaconescu
CUPRINS
Introducere, 10
I. Ce este Python ?
II. Avantajele și dezavantajele limbajului Python
III. Utilizarea Python în proiectarea aplicațiilor
IV Instalarea Python
V Medii de dezvoltare pentru proiectele Python
V.1 Conceptul de mediu de dezvoltare
V.2 IDLE Python
V.3 Spyder
V.4 PyCharm
V.5 Visual Studio Code
V.6 Eclipse + PyDev
V.7 Pyzo
V.8 Sublime Text
V.9 Thony
V.10 Alte medii de dezvoltare IDE
VI Proiectul Jupyter
VII Interpretoare și compilatoare Python
3.2 Modulele
3.2.1 Importarea modulelor în Python
3.2.1.1 Instrucțiunea import
3.2.1.2 Instrucțiunea from … import
3.2.1.3 Importarea tuturor numelor
3.2.2 Calea de căutare (path) a modulelor
3.2.3 Reîncărcarea unui modul
3.2.4 Funcția built-in dir()și atributul '__name__'
3.3 Pachetele
3.3.1 Definiția pachetului
3.3.2 Importul modulelor din pachete
3.4 Întrebări, exerciții și probleme
5. Liste, 110
5.1 Precizare
5.2 Definiția și crearea listelor
5.3 Accesarea elementelor unei liste
5.3.1 Indexarea unei liste
5.3.2 Indexarea negativă
5.4 Felierea (slicing)
5.5 Schimbarea sau adăugarea elementelor unei liste
5.6 Ștergerea sau eliminarea elementelor dintr-o listă
5.7 Tabel cu metodele obiectului listă
5.8 Lista comprehensivă
5.9 Alte operații cu liste în Python
5.9.1 Testul de apartenență la listă
5.9.2 Parcurgerea unei liste
5.10 Tablourile 1D în Python. Modulul array
5.10.1 Generalități
5.10.2 Operațiile de bază cu un tablou (array) unidimensional (1D)
5.11 Liste de liste (tablourile bidimensionale 2D) în Python
5.11.1 Operațiile de bază cu tablourile 2D
5.11.2 Netezirea listelor de liste
5.12 Întrebări, exerciții și probleme
6. Tupluri, 125
6.1 Definiția și crearea tuplurilor
6.2 Accesarea elementelor unui tuplu
6.2.1 Indexarea pozitivă
6.2.2. Indexarea negativă
6.2.3 Slicingul (felierea) tuplurilor
4
7. Șiruri, 133
7.1 Conceptul de șir
7.2 Crearea șirurilor
7.3 Accesarea caracterelor unui șir
7.4 Modificarea sau ștergerea unui șir
7.5 Operații cu șiruri
7.5.1 Concatenarea a două sau mai multe șiruri
7.5.2 Iterarea unui șir
7.5.3 Testarea apartenenței la șir
7.5.4 Funcții pre-construite pentru lucrul cu șiruri
7.6 Formatarea șirurilor
7.6.1 Secvența Escape
7.6.2 Șiruri brute (raw)
7.6.3 Formatarea șirurilor cu metoda format()
7.6.4 Formatare clasică (specifică limbajului C standard)
7.6.5 Formatarea cu prefix f (metoda f-strings)
7.7 Metodele uzuale ale șirurilor
7.8 Întrebări, exerciții și probleme
9. Dicționare, 152
9.1 Definiția și crearea dicționarelor
9.2 Accesarea elementelor unui dicționar
9.3 Modificarea și adăugarea elementelor unui dicționar
9.4 Eliminarea elementelor din dicționar
9.5 Metodele obiectului dicționar
9.6 Comprehensiunea dicționarelor
9.7 Alte operații cu dicționarele
9.7.1 Testul de apartenență la dicționar
9.7.2 Parcurgerea unui dicționar
9.7.3 Funcții built-in pentru dicționare
9.8 Întrebări, exerciții și probleme
5
Glosar, 408
Bibliografie, 414
Prefață
Dacă se face o simplă căutare, pe Internet, a lucrărilor referitoare la limbajul Python, se
descoperă imediat o cantitate impresionantă de tutoriale, cursuri online, cărți, aplicații, sau
documentații, în majoritate în limba engleză. În acest context se pune întrebarea: de ce această
nouă lucrare?
Răspunsul este simplu: această lucrare se dorește a fi un mijloc de abordare, de utilitate cît mai
mare (ghid, manual și îndrumar practic), pentru cei care doresc să se inițieze sau să-și
îmbunătățească cunoștințele referitoare la limbajul Python și aplicațiile sale, în limba română.
- Un ghid, pentru că asigură o privire de ansamblu asupra fenomenului Python (fără însă
să epuizeze toate aspectele).
- Un manual, pentru că încearcă să fie o lucrare de referință în studiul și utilizarea Python.
- Un îndrumar, deoarece oferă un număr mare de exemple și exerciții de utilizare a
limbajului Python și a aplicațiilor sale.
Ideal, pentru parcurgerea lucrării, ar fi utilă inițierea prealabilă în limbajul “C” (pentru
asigurarea unor cunoștințe generale despre limbaje) și/sau ”C++” (pentru asigurarea unor
cunoștințe generale despre tehnologia programării orientate spre obiecte). În acest ultim caz,
abordarea conținutului poate fi făcută “liniar”, capitol după capitol.
Totuși, lucrarea poate fi utilizată și pentru inițierea într-un limbaj de programare, fără
cunoașterea anterioară a limbajului C, sau a unui alt limbaj. În acest caz, capitolele se pot
parcurge oarecum “neliniar” și iterativ. Python este un limbaj orientat total pe obiecte, dar
abordarea conceptelor referitoare la obiecte nu se face din primele capitole ale lucrării. Din
acest motiv, se recomandă cititorului să nu insiste asupra unor concepte sau modele pe care nu
le înțelege în primele capitole, dar să revină la ele după studiul capitolelor 11 (generalități POO)
și 12 (clase și obiecte).
Partea I se referă la învățarea limbajului Python (primele capitole până la al 19-lea, inclusiv,
conțin noțiunile fundamentale, atât cele comune diferitelor limbaje de programare, cât și cele
specifice: tipuri de date, șiruri, liste, tupluri, dicționare, etc.). Partea I este accesibilă și poate fi
studiată și de elevii de gimnaziu sau liceu.
Partea a II-a conține o prezentare limitată a unor aspecte ale “ecosistemului” Python (capitolele
20 – 26). Este utilă studenților sau specialiștilor care caută un mediu de programare fără costuri
(“open source”), echivalent cu MATLAB, sau mai puternic, îndeosebi pentru aplicații științifice,
dar nu numai. Sunt abordate în cadrul acestei părți chestiuni ca programarea unor interfețe
grafice interactive, reprezentarea și prelucrarea datelor numerice/științifice, elemente de
procesare a imaginilor, etc.
Adresa de email a autorului, dedicată lucrării, pentru primirea diverselor observații, sugestii,
aprecieri, sau discutarea anumitor aspecte ale cărții, este: [email protected].
INTRODUCERE
I. Ce este Python ?
Python este un limbaj de programare deja foarte cunoscut și apreciat, având capacități
superioare, specifice limbajelor de nivel înalt. La popularitatea sa au contribuit atât ușurința de
învățare a sintaxei, cât și portabilitatea sa.
Python a fost dezvoltat de Guido van Rossum la Stichting Mathematisch Centrum în Olanda.
Limbajul a fost scris ca un succesor al limbajului de programare denumit ‘ABC’, iar prima
versiune a fost disponibilă în 1991.
Numele “Python” a fost dat de Guido van Rossum după un show TV denumit “Monty Python’s
Flying Circus” care se difuza la acea vreme și care plăcuse autorului.
Python este un software ușor accesibil care poate fi descărcat și instalat gratuit pe orice sistem
de operare de la www.python.org.
Python împrumută caracteristici atât din limbajele tradiționale ca Java și C, dar și din limbajele
funcționale.
Este un limbaj interpretat, codul sursă este mai întâi convertit într-un bytecode, apoi executat
de mașina virtuală Python. Sunt disponibile și compilatoare pentru obținerea fișierelor
executabile din programele sursă.
Esența filozofiei limbajului este pe scurt prezentată în documentul "PEP 20 – The Zen of
Python". (Python Enhancement Proposals, Python Software Foundation, Tim Peters, 2004),
care include și următoarele aforisme (în engleză):
- Beautiful is better than ugly.
- Explicit is better than implicit.
- Simple is better than complex.
- Complex is better than complicated.
- Readability counts.
II. Avantajele și dezavantajele limbajului Python
Avantaje
Python este ușor, rapid-inteligibil, multi-orientat, vast, robust, adaptativ, scalabil, concentrat.
Este ușor de învățat și înțeles - deoarece sintaxa limbajului Python este mai simplă, ceea ce
ușurează învățarea și înțelegerea.
Este un limbaj multi-orientat - deoarece permite mai multe tipuri sau stiluri de programare:
structurată, orientată spre obiecte și funcțională.
Este vast – deoarece dispune de un număr uriaș de module (“ecosistemul” Python) care acoperă
multe aspecte ale activităților de proiectare și realizare de programe informatice. Aceste module
sunt ușor disponibile, ceea ce face din Python un limbaj extensibil.
Sustenabil – deoarece are susținerea unei comunități open source foarte puternice. Acest lucru
ajută la fixarea rapidă a bug-urilor, ceea ce face ca Python să fie și robust și adaptiv.
Este scalabil - deoarece asigură o structură îmbunătățită pentru a permite și realizarea de
programe și aplicații mari, nu numai scripturi dezvoltate în shell.
11 Introducere
Este eficient și concentrat – se spune că programele scrise în Python sunt cu 20-90% mai scurte
decât în alte limbaje clasice.
O explicație a succesului uriaș al Pythonului s-ar datora, după unele studii și cercetări, ușurinței
utilizării pe care ar fi generat-o identarea obligatorie și eliminarea acoladelor tradiționale
pentru delimitarea blocurilor, atât de obișnuite și necesare în limbajele C/C++ și Java.
Python a fost primul limbaj de programare în care s-a conștientizat pe deplin ideea că indentarea
este un factor foarte important în ușurarea efortului de a scrie programe.
Indentarea a fost de mult timp o tehnică utilizată în scrierea programelor, mai în toate limbajele
de programare, având scopul de a asigura o anumită claritate în reprezentarea algoritmului prin
instrucțiuni și a ușura înțelegerea acestuia la momentul citirii sau recitirii unui program.
Indentarea este de fapt o organizare în planul vederii a structurii ierarhice și arborescente a
unităților care compun programul, cu efecte vizuale pe direcțiile orizontală și verticală. În
limbaje ca C/C++/Java, existența structurii ierarhic-arborescente este garantată prin utilizarea
acoladelor, indentarea fiind doar recomandată și opțională. Ca urmare al utilizării în plus a unui
număr mare de simboluri, perechi de acoladă, se produce efectul nedorit de obosire a vederii și
diminuarea atenției și chiar generarea unui număr mai mare de erori datorită necesității de a
contabiliza mental perechile de acoladă. Mai mult, apare un efect pervers al neobligativității
indentării constând în tentația oferită programatorului de a nu utiliza deloc indentarea, cel puțin
pe unele porțiuni, sau de a o utiliza neuniform în cadrul programului, mai ales dacă acesta este
supus unor frecvente modificări, de către mai mulți programatori. Neuniformitatea este însă
obositoare, iar alternarea aleatorie a unor reguli de indentare poate conduce la unele dificultăți
în concentrarea programatorului la munca sa.
Mai mult de atât, se verifică practic că este mult mai ușoară structurarea vizuală a unităților
programului, comparativ cu aceea prin simboluri de tip acoladă. Efectul este cumva asemănător
aceluia sugerat de un vechi proverb chinezesc: “O imagine valorează mai mult ca o mie de
cuvinte.” De fapt, procesarea de creier a unei imagini se face mult mai rapid comparativ cu
analiza logică a unei hărți de simboluri.
Sentimentul de ordine și aspectul de lucru organizat pe care îl produce indentarea, mai ales
indivizilor care prin natura lor sunt oarecum dezordonați, dă o adevărată dependență față de
limbajul Python.
Python este considerat în prezent ca fiind un limbaj utilizat pe larg de comunitatea
științifică, îndeosebi de accea implicată activ în cercetările referitoare la inteligența
artificială, fiind considerat un înlocuitor pentru Matlab. Combinația Python+Numpy
(Numerical Python) +Matplotlib (Plotting Library) +SciPy (Scientific Python) este utilizată în
prezent pe scară largă ca o soluție alternativă la Matlab, fiind suficient de modernă și
cuprinzătoare. În plus, caracterul open source este un important avantaj pentru ca Python și
ecosistemul său să fie preferat sistemului Matlab.
Sintetic, Python poate fi apreciat ca:
1. Simplu și ușor de invătat.
2. Are un ciclu rapid de dezvoltare a proiectelor (easy and fast prototyping).
3. Mod interactiv de lucru.
4. Portabil (PortablePython poate fi instalat pe un stick usb).
5. Dispune de biblioteci pentru programarea GUI.
6. General extensibil.
12 Introducere
Dezavantaje
Principalul dezavantaj este viteza mai mică de execuție, deoarece nu este un limbaj integral
compilat, ci interpretat. De fapt, este considerat de către unii specialiști ca fiind “interpretat și
compilat”, luîndu-se în considerare și faza de obținere a unui bytecode înainte de execuție.
Totuși, viteza de lucru poate fi îmbunătățită prin compilatoare (sau “cross-compilere”).
IV Instalarea Python
Versiunile limbajului Python au apărut conform următoarei cronologii:
1991, februarie - Python 0.9.0
1994, ianuarie – Python 1.0
2000, octombrie – Python 2.0
2008, decembrie – Python 3.0
2016, decembrie – Python 3.6
2018, mai – Python 3.7
2019, octombrie – Python 3.8
2020, octombrie – Python 3.9
2020 – Python 2.0 nu a mai fost susținut (end-of-life).
2021, octombrie – Python 3.10
13 Introducere
Python3 este în prezent preinstalat pe versiunile curente de Linux (Ubuntu). Se apelează simplu
în terminal, în linia de comandă, “python3”, după care apare promterul binecunoscut “>>>”.
Dacă se utilizează o versiune mai veche de Linux/Ubuntu, se accesează www.python.org și se
descarcă codul arhivat corespunzător versiunii de Python dorită. Se continuă instalarea,
presupunând un utilizator non-user, cu privilegii sudo, etc.
Vizualizare rapidă pachete-module-clase instalate. Se utilizează opțiunile din meniu Path Browser și
Module Browser.
Identare inteligenta: opțiuni diverse în meniu pentru identare, tabulare și colorare.
Numerotare linii: opțiune ON/OFF
Opțiuni configurare: Da
Împerechere automată paranteze-acolade: Nu
Completare-automată-cod: Nu (la cerere prin Ctrl +Sp)
Redare grafică:Da (tkinter)
Integrare surse/proiecte: Nu
Integrare Version Control: Nu
Observație. Schimbarea fondului din întunecat (IDLE dark) în luminos (IDLE Classic) se face
din Options/Configure IDLE/Highlights.
>>>for i in range(n):
... print(i*'*')
... print((n-i)*'%')
%%%%%
*
%%%%
**
%%%
***
%%
****
%
18 Introducere
V.3 Spyder
Spyder este un IDE puternic pentru mediul științific (scris în Python), pentru programarea în
limbajul Python, de tip open source.
Integrează un număr mare de pachete importante pentru mediul științific, printre care: NumPy,
SciPy, Matplotlib, Pandas, Ipython, Sympy, etc. De asemenea, integrează: console interne
multiple, history log, on line help browser, etc., permițînd vizualizarea de pachete și
documentații în interiorul IDE, analizor, debugger, etc. Include plug-in-uri, de exemplu pentru
vizualizare și editare de Notebooks-Jupiter (cu conda sau pip) . Stabilirea directorului curent
de lucru poate fi selectat din meniul tools>preferences>current working directory, iar
opțiunea de interfață întunecată (dark) din tools>preferences>appearance>interface
theme.
Completare-auto-cod: Da
Redare grafică: Da, utilizează Qt, PyQT, sau PySide pentru GUI.
Integrare surse/proiecte: Da
Integrare Version Control: Nu
Plug-in-uri: DA
https://docs.spyder-ide.org/index.html
Observație. Sursa programului Python poate fi împărțită în celule care pot fi executate
individual, sau pas cu pas urmărind rezultatele, sau integral. Șirul delimitator pentru o celulă
este “#%%”, același ca în Jupyter Notebook.
V.4 PyCharm
PyCharm este considerat cel mai complet IDE pentru Python. Este oferit în trei variante:
Profesional Edition (plătit), Community Edition (Gratuit) și Educational Edition (Gratuit) (din
2010) de firma JetBrains.
În timp ce PyCharm CE și PyCharm EE se pot utiliza doar cu Python, PyCharm PE poate
dezvolta proiecte și pentru alte procesoare software, fie aparținând ecosistemului Python
(Django, Web2Py, Flask, Pyramid, etc.), fie diferite (JavaScript, PHP, etc.).
PyCharm este integrat în distribuția Anaconda.
Date tehnice:
Producător: Compania cehă JetBrains
Comercial/free: Apache Licence (PCE), proprietary licence (PPE)
Cross-platformă: Windows, macOS and Linux
Specific/Universal: specific Python, dar și alte limbaje ca JavaScript, PHP
Multilingv: Engleză
Vizualizare rapidă pachete-module-clase instalate.
Identare inteligenta: Da
Numerotare linii: Da
Opțiuni configurare: Da
Împerechere automată paranteze-acolade: Da
Completare-auto-cod: Da
Redare grafică:Da
Integrare surse/proiecte: Da
Integrare Version Control: Da
Observație. Schimbarea fondului din întunecat în luminos se face deschizând
Settings/Preferences cu Ctrl +Alt + S (File/Setings), selectând Appearance&Behavior și apoi
Background Image Button.
20 Introducere
consolă interactivă, găsire referințe (Ctrl + Shift +G), etc. Sunt disponibile shortcuts-uri la
tastatură pentru toate acțiunile cu mouse-ul.
PyDev pentru Eclipse are o variantă denumită LICLIPSE, care acceptă Django, HTML, CSS și
JavaScript, dar necesită licență plătită.
V.7 Pyzo
Este un mediu de dezvoltare pentru Python gratuit și open source.
Este scris în Python și utilizează toolkitul GUI QT.
Este orientat catre simplitate și ușurință în dezvoltarea proiectelor.
V.9 Thony
Thony este un IDE foarte prietenos pentru începătorii care studiază Python pentru învățare. În
acest scop este proiectat special, avind un debugger și alte caracteristici de mare ajutor. Este
gratuit, fiind dezvoltat de Universitatea din Tartu, Estonia.
Wing
Wing este o soluție performantă de IDE pentru Python cu licență plătită de la WingWare. Este
multiplatformă (Windows, Linux și MacOS X). Dispune de un utilitar de cod inteligent și unelte
foarte bune de punere la punct, etc.
Sunt oferite 3 versiuni, pentru 3 tipuri diferite de utilizatori:
Wing 101 pentru începători (licență gratuită), cu următoarele facilități:
Depanarea codului, inclusiv interactivă, raportarea excepțiilor și a urmăririi, vizualizare
variabile și stivă, etc.
Wing Personal (licență gratuită pentru uz educațional), cu facilități suplimentare:
Depanare multithreading și alte facilități complexe și avansate de depanare și punere la punct,
etc.
Wing Pro pentru programatorii profesioniști (licență comercială), are aproape toate facilitățile
posibile pentru un IDE, inclusiv utilizare pentru Django, Flask, Jupyter, matplotlib, web2py,
Plone, Zope, Docker, AWS, Vagrant, Raspberry Pi, Blender, Unreal Engine, Nuke, etc.
Cloud9
Cloud9 este un IDE cu aproape toate facilitățile posibile și care permite lucrul colaborativ în
Cloud (oriunde, oricând), în mai multe limbaje, inclusiv Python, C, C++, PHP, Ruby, Perl,
JavaScript cu Node.js, Go, etc.
Dispune de unul dintre cele mai avansate editoare cu completarea codului, etc. Poate dezvolta
proiecte în Django și Flask.
Din 2016 a fost achiziționat de Amazon și poate fi utilizat necomercial doar cu un cont AWS
(Amazon Web Services).
Eric
Eric este un IDE open source, deci gratuit, scris în Python, pentru Python și QT. Deși este un
proiect necomercial, are toate caracteristicile unui software profesional, comparabil cu multe
IDE-uri comerciale. Creatorul său se numește Detler Offenback.
24 Introducere
VI Proiectul Jupyter
Proiectul Jupyter a fost demarat în 2014 cu numele IPython Projects ca un mediu interactiv
open-source pentru calculul științific și inteligența artificială. Ideea era de pune la îndemâna
cercetătorilor un software open-source bazat pe standarde de asemenea de tip open și servicii
pentru interacționarea cu calculatorul, bazate pe web. In primul rând, el permite utilizatorului
să includă, pe lângă codul Python și text formatat (markdown), vizualizări statice și dinamice,
widgeturi JavaScripts, etc. Documentul rezultat poate fi exportat ca simplu script Python, PDF,
sau HTML.
Nucleul IPython acceptă și alte limbaje de programare, ca Julia și R. Chiar denumirea sa arată
acest lucru: Jupyter = JUlia, PYThon, R. El a evoluat, acceptând și alte limbaje de programare
utilizate în prezent, ajungând la peste 40.
Prima generație de software a fost Jupyter Notebook, dedicat dezvoltării de cod, inițial în
limbajul de programare Python. A doua generație este Jupyter Lab care își propune să includă
componente de tip notebook, cod sursă și date, în mai multe fluxuri de lucru.
Comunitatea Jupyter (jupyter.org) este foarte largă și susține numeroase proiecte, găzduite de
GitHub, de exemplu: jupyter, ipython, jupyterhub, jupyterlab, jupyter-widgets, jupyter-server,
jupyter-xeus, voila-dashboards, binder-examples, jupyter-resources, etc.
Observație. Documentele de lucru Jupyter se rulează în browser, pe calculatorul utilizatorului
(serverul Jupyter este instalat pe propriul calculator al utilizatorului, asigurându-se astfel
protecția datelor). Totuși, se poate efectua o sesiune de lucru și la distanță, pe calculatoarele
universităților sau ale firmelor, unde poate fi instalad serverul Jupyter de interes. În acest caz,
securitatea disponibilă este cea generală asigurată de softurile IT utilizate.
Observație. Un exemplu special de utilizarea Jupyter este rularea aplicațiilor Python pe colab,
supercalculatorul oferit de Google pentru utilizatorii independenți care dezvoltă aplicații intens
consumatoare de resurse, în general din domeniul inteligenței artificiale. Este util pentru rularea
aplicațiilor care solicită mari resurse de calcul de tip GPU.
Instalarea serverului Jupyter se face simplu în Windows sau Linux/Ubuntu cu utilitarele pip și
conda, de exemplu:
>pip install jupyter
Jupyter Notebook
Jupyter Notebook reprezintă prima generație a proiectului Jupyter.
Jupyter Notebook poate fi startat în Windows și din linia de comandă a sistemului de operare:
C:\cale director de lansare>jupiter notebook
Jupyter Notebook este o aplicație client-server. Pagina de lucru se deschide în browserul web
implicit de pe calculator la o adresă URL locală, de exemplu: http://127.0.0.1:8888
(http://localhost:8888).
Pagina de lucru este un dashboard, arătând notebook-urile existente în directorul de lucru (care
este chiar cel de lansare), figura 1.8. Extensia implicită este .ipynb (probabil prescurtarea de
la ipython notebook), dar sunt admise și alte extensii, de exemplu .py.
Caracteristici generale:
25 Introducere
O pagină nouă de lucru (notebook nou) se deschide selectând new, figura 1.9.
Selectând Python 3 se va deschide o altă fereastră, unde se poate introduce și rula noul cod
Python, în celule, figura 1.10.
26 Introducere
Jupyter-Lab
Jupyter-Lab (apărut în 2018) este a doua generație de interfețe bazate pe web a proiectului
Jupyter. El va înlocui probabil Jupyter Notebook, dar în prezent suportă și documentele de tip
Jupyter Notebook în același mod ca și Jupyter Notebook.
Jupyter-Lab permite configurarea și aranjarea interfeței de către user corespunzător interesului
său, integrând mai multe fluxuri de lucru. De asemenea admite instalarea unor plug-in-uri
pentru extinderea facilităților.
Jupyter-Lab (figura 1.11) se poate lansa de asemenea din linia de comandă, astfel:
>jupyter-lab
CAPITOLUL 1
Cuvintele cheie sunt cuvinte a căror utilizare este rezervată în Python. Cuvintele cheie definesc
sintaxa și structura limbajului Python.
Nu se poate utiliza un cuvânt cheie ca nume de variabilă, nume de funcție sau ca orice alt
identificator.
În Python, cuvintele cheie sunt case sensitive, ceea ce înseamnă că la scrierea lor se sesizează
diferența dintre caracterele majuscule și minuscule.
În total, sunt 35 cuvinte cheie în Python. În timp, numărul lor s-a modificat destul de puțin.
Toate cuvintele cheie cu excepția lui True, False și None sunt scrise în lowercase. De exemplu:
False, None, True, and, as, assert, async, etc.
Observație. Lista cuvintelor cheie poate să difere în funcție de versiunea Python instalată. Se
poate obține lista completă în versiunea current utilizată de Python prin comenzile:
>>> import keyword
>>> print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break',
'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally',
'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal',
'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
1.1.2 Identificatorii
Definiție. Un identificator este un nume dat unei entități variabilă, funcție, clasă, etc., rolul său
fiind să diferențieze o entitate de alta.
Identificatorii pot fi o combinație de litere mici (a – z), mari (A – Z), digiți (0 – 9), sau
underscore ( _ ) . Exemple corecte: mCifre, linie_1 și valoare_noua_citita .
1. Un identificator poate avea orice lungime.
2. Un identificator nu poate începe cu o cifră. Numele 5numar este invalid, dar numar5 este
permis.
3. Cuvintele cheie nu pot fi utilizate ca identificatori.
Exemplul 1.1 #identificatorul nu poate fi cuvânt cheie
lambda = 7
Output
File "<interactive input>", line 1
29 Elemente introductive în limbajul Python
lambda = 7
^
SyntaxError: invalid syntax
Output
File "<interactive input>", line 1
a% = 0
^
SyntaxError: invalid syntax
Pentru programatorii români sunt câteva aspecte de terminologie care trebuie lămurite. De
ajutor poate fi o comparație cu limbajul C, mai vechi și adesea cunoscut înainte de studiul
limbajului Python prin sistemul de educație existent.
În limbajul C există clar o diferențiere, nu numai lingvistică, dar și ca întrebuințare, între
termenii instrucțiune, declarație și definiție.
În limbajul C, în primul rând prin “instrucțiune” sunt denumite cuvintele cheie de tipul for,
if, while, etc. Apoi, în limbajul C, se face distincție între definiție și declarație.
Declarația care se poate referi, de exemplu, la tipul unei variabile, este doar o informare a
compilatorului pentru ca acesta să știe câtă memorie trebuie alocată pentru variabila respectivă
(dar inițializarea încă nu este făcută).
Definiția unei variabile presupune atât alocarea de memorie cât și inițializarea acesteia..
Atât declarația, cât și definiția au în limbajul C scopul de a preciza de la început tipul variabilei,
care nu se mai poate modifica în timpul programului.
În Python lucrurile sunt diferite. Python este un limbaj din categoria “cu deducerea tipului”
(limbaj type-inferred) la execuție, pentru care nu este necesară definirea tipului variabilei
la declarare.
În Python, instrucțiunea pe care o poate executa interpretorul este denumită declarație
(statement). De exemplu, a = 1 este o declarație de atribuire, deci o definiție. Declarațiile if,
for, while, etc. sunt alte tipuri de declarații ce vor fi discutate mai târziu.
Totuși în limba română, pentru Python, nu este greșită și utilizarea termenului “instrucțiune”.
În această lucrare, termenii “instrucțiune” și “declarație” vor fi utilizați cu același sens.
Declarația multi-linie, caracterul de continuare
În Python, sfârșitul unei instrucțiuni este marcată printr-un character newline. Este însă posibil
să se extindă o declarație peste mai multe linii utilizând caracterul de continuare (\). De
exemplu:
a = 1 + 2 + 3 + \
4 + 5 + 6 + \
7 + 8 + 9
30 Elemente introductive în limbajul Python
Aceasta a fost o continuare explicită de linie. În Python, continuarea de linie este implicită în
interiorul parantezelor și acoladelor ( ), [ ], { }. De exemplu, se poate implementa implicit
multilinia de mai sus astfel:
a = (1 + 2 + 3 +
4 + 5 + 6 +
7 + 8 + 9)
De asemenea, se pot pune mai multe instrucțiuni pe o singură linie utilizând simbolul (;):
a = 1; b = 2; c = 3
Cele mai multe din limbajele de programare, ca de exemplu C, C++, sau Java utilizează
acoladele {} pentru a defini un bloc de cod. Python, în schimb, utilizează indentarea.
Un bloc de cod (corpul unei funcții, bucle, etc.) începe cu prima linie indentată și se termină la
prima linie neindentată. Mărimea indentării poate fi oricare, dar trebuie păstrată pentru tot
blocul.
În general, ca mărime a indentării se utilizează patru spații, obținute folosind tasta tab. De
exemplu:
for i in range(1,11):
print(i)
if i == 5:
break
Utilizarea indentării face ca programul scris în Python să apară curat și clar. Rezultatul
înseamnă programe coerente, având un aspect similar.
Indentarea poate fi omisă în continuările de linie, dar opțiunea de indentare rămâne o idee bună,
deoarece face codul mai ușor de citit. De exemplu:
if True:
print('Start calcul')
a = 5
și
if True: print('Start calcul'); a = 5
ambele secvențe sunt corecte și fac același lucru, dar prima formă este mai clară.
În cazul unei indentări incorecte se afișează IndentationError.
Observație. O indentare greșită poate conduce la erori de programare uneori greu de identificat
și eliminat.
Observație. Regulile de bună practică în programare recomandă totuși evitarea utilizării tastei
TAB pentru identare, preferându-se folosirea spațiilor.
31 Elemente introductive în limbajul Python
1.2.3 Comentariile
Comentariile sunt foarte importante în timpul redactării unui program. Acestea descriu ce se
întâmplă în interiorul programului, astfel încât o persoană care citește codul sursă poate să-l
înțeleagă mai repede, fără dificultate.
Detaliile cheie ale unui program recent scris se pot uita peste ceva timp. În acest caz, este
întotdeauna eficient să se explice acele detalii prin intermediul comentariilor. Așadar, a găsi
timp pentru a explica aceste detalii sau conceptele programului sub formă de comentarii este
întotdeauna o idee bună.
În Python, se utilizează simbolul hash (#) la începutul scrierii unui comentariu. Comentariul se
extinde până la caracterul rând nou / newline.
Interpretorul Python ignoră comentariile.
#Acesta este un comentariu
print('Salut!')
Comentarii multi-linie
Unele comentarii se pot extinde pe mai multe linii.
Pentru realizarea comentariilor multilinie sunt disponibile două metode.
A. O metodă de a realiza acest lucru este utilizarea simbolului (#) la începutul fiecărei linii. De
exemplu:
#De aici incepe calculul
#perimetrului și ariei pentru următoarele
#figuri geometrice: triunghi, patrat și cerc.
B. O altă cale de a face același lucru este utilizarea ghilimelelor triple (simple sau duble,
triplate), adică ''' sau """.
Ghilimelele simple (apostroful) triplate sau ghilimele duble triplate sunt folosite în general
pentru șiruri multilinie, dar și pentru comentarii multilinie.
Observație. Comentariile multilinie nu generează cod obiect suplimentar; excepție este doar
cazul docstring-urilor.
"""Acesta
este un exemplu
de comentariu multilinie"""
Docstring-urile sunt associate unui obiect prin atributul __doc__. Acest lucru permite accesul
la docstringul obiectului, astfel:
32 Elemente introductive în limbajul Python
def incr(n):
"""Functia incrementeaza valoarea """
return n + 1
print(incr.__doc__)
Output
Functia incrementeaza valoarea
Definiție. O variabilă este o locație de memorie având un nume, utilizată pentru memorarea
unei date în memorie și al cărei conținut poate fi schimbat mai târziu în program. De exemplu:
lungime = 21
S-a creat variabila cu numele lungime. S-a atribuit valoarea 21 acestei variabile. Această
valoare se poate schimba:
lungime = 21
lungime = 23.7
Observație: În Python, nu se pot atribui în mod real valori variabilelor. Ceea ce se întâmplă de
fapt este memorarea în variabilă a referinței la obiect (adresa obiectului).
Atribuirea de valori variabilelor în Python
Atribuirea de valori variabilelor se face prin operatorul de atribuire “=”.
Examplul 1.3 #Declararea și atribuirea de valoare unei variabile
titlu = "Programarea PLC-urilor"
print(titlu)
Output
Programarea PLC-urilor
În secvența program de mai sus, s-a atribuit valoarea Programarea PLC-urilor variabilei
titlu. Apoi s-a imprimat valoarea atribuită variabilei titlu, adică Programarea PLC-
urilor.
Observație: Deoparece Python este un limbaj din categoria “cu deducerea tipului” (limbaj type-
inferred), pentru care nu este necesară definirea tipului variabilei, Python știe automat că
Programarea PLC-urilor este un șir și declară (neexplicit) variabila titlu ca șir.
Output
robofun.ro
emag.ro
a, b, c = 8, 4.4, "abcdefg"
print(a)
print(b)
print(c)
Output
8
4.4
Abcdefg
Output
perimetru
perimetru
perimetru
1.3.2 Constante
microMotor
PrimulProiect
2. Este recomandat să se creeze nume care au un sens. De exemplu, viteza are mai mult sens
comparativ cu v.
3. In cazul numelor de variabile formate din două cuvinte, se utilizează caracterul underscore
( _ ) pentru a le separa. De exemplu:
anul_nasterii
data_angajarii
1.3.4 Literale
Definiție. Literalul este o dată conținută într-o variabilă sau constantă. Python conține mai
multe tipuri de literale, după cum se prezintă în continuare.
Literale Numerice
Literalele numerice sunt immutable (nu se pot schimba). Literalele numerice sunt de trei tipuri
numerice diferite: Integer, Float și Complex.
Exemplul 1.8 #Utilizarea literalelor numerice
a = 0b1011 #literal binar
b = 123 #literal zecimal
c = 0o620 #literal octal
d = 0x12c #literal hexazecimal
#Literal float
float_1 = 22.7
float_2 = 2.5e3
#Literal complex
z = 5.58j
print(a, b, c, d)
print(float_1, float_2)
print(x, x.imag, x.real)
Output
11 123 400 300
22.7 2500.0
5.58j 5.58 0.0
- În cazul variabilelor complexe (de exemplu, atribuirea valorii 7+3.33j variabilei x), se va
utiliza literalul real (x.real) și literalul imaginary (x.imag) pentru a crea părțile reale și
imaginare ale numerelor complexe.
Literale șir
Definiție. Un literal de tip șir este o secvență de caractere cuprinsă între apostrofuri. Se pot
utiliza apostroful simplu ('), dublu (") (ghilimelele se pot considera apostrofuri duble), sau
triplu (''') pentru șiruri. Un literal de tip character este un singur character cuprins între
apostrofuri simple sau duble.
Exemplul 1.9 #Utilizarea literalelor șir
sir1 = 'acesta este un sir delimitat de apostrof'
sir2 = "acesta este un sir delimitat de ghilimele"
char1 = 'A'
char2 = "B"
sir_multilinie1 = '''acesta este
un sir
multilinie delimitat de apostrof triplat'''
sir_multilinie2 = """acesta este
un sir
multilinie delimitat de ghilimele triplate"""
unicode = u"\u00dcnic\u00f6de"
raw_sir = r"sir \n brut"
print(sir1)
print(sir2)
print(char1)
print(char2)
print(sir_multilinie1)
print(sir_multilinie2)
print(unicode)
print(raw_sir)
Output
acesta este un sir delimitat de apostrof
acesta este un sir delimitat de ghilimele
A
B
acesta este
un sir
multilinie delimitat de apostrof triplat
acesta este
un sir
multilinie delimitat de ghilimele triplate
Ünicöde
sir \n brut
Observație. (1) Șirul u"\u00dcnic\u00f6de" este un literal Unicode care acceptă și alte
caractere decât cele engleze. În acest caz, \u00dc reprezintă Ü și \u00f6 reprezintă ö.
(2)r"sir \n brut" este un literal șir brut (raw), în care caracterul special \n inclus în
șir este interpretat ca text.
Literale booleene
Definiție. Un literal Boolean poate avea una dintre cele două valori: True sau False.
36 Elemente introductive în limbajul Python
print("x =", x)
print("y =", y)
print("a:", a)
print("b:", b)
Output
x = True
y = False
a: 11
b: 10
Valoarea lui x este True deoarece 1 este egal cu True, iar valoarea lui y este False deoarece
1 nu este egal cu False.
Observație. Se pot utiliza True sau False în expresii numerice ca valori. Valoarea lui a este
11 deoarece se adaugă True (=1) la 10. Similar, b este 10 deoarece se adună False (=0) la 10.
Literale speciale
Definiție. Literalul special, None, se utilizează pentru a preciza că un câmp nu a fost creat încă.
Exemplul 1.11 # Utilizarea literalelor special în Python
bere = "In stoc"
vin = None
def produse(x):
if x == bere:
print(bere)
else:
print(vin)
produse(bere)
produse(vin)
Output
In stoc
None
În programul de mai sus, se definește o funcție produse. Funcția produse, afișează In stoc
când argumentul este bere, apoi când argumentul este vin, afișează None.
Colecții de literale
Există patru colecții diferite de literale: List, Tuple, Dict, și Set.
Exemplul 1.12 #Utilizarea colecțiilor de literale în Python
fructe = ["mar", "par", "gutuie"] #lista
numere = (1, 2, 3) #tuplu
alfabet = {'a':'apa', 'b':'bilet', 'c':'cui'} #dictionar
vocale = {'a', 'e', 'i' , 'o', 'u'} #set
print(fructe)
print(numere)
print(alfabet)
print(vocale)
37 Elemente introductive în limbajul Python
Output
['mar', 'par', 'gutuie']
(1, 2, 3)
{'a': 'apa', 'b': 'bilet', 'c': 'cui'}
{'e', 'a', 'o', 'i', 'u'}
În programul de mai sus se creează o listă de fructe, un tuplu de numere, un dicționar dict având
valori cu chei destinate fiecărei valori și un set de vocale.
Output
5 este de tip <class 'int'>
2.0 este de tip <class 'float'>
(1+2j) este de tip complex ? True
Întregii pot fi de orice lungime, limitarea este dată doar de memoria disponibilă.
38 Elemente introductive în limbajul Python
Un număr în virgulă flotantă are precizia de până la 15 zecimale. Partea întreagă și partea
flotantă/zecimală sunt separate de punct. 1 este un întreg, 1.0 este un număr în virgulă flotantă.
Numerele complexe sunt scrise în forma x + yj, unde x este partea reală, iar y este partea
zecimală.
Exemplul 1.14 #lungimea nelimitată a întregilor și precizia de 15 zecimale
>>> a = 1234567890123456789
>>> a
1234567890123456789
>>> b = 0.1234567890123456789
>>> b
0.12345678901234568
>>> c = 1+2j
>>> c
(1+2j)
Se observă că variabila flotantă b apare trunchiată.
1.4.2 Tipul boolean
În Python, o variabilă booleană este declarată prin cuvintele cheie True și False.
Observație. Python atribuie tipul la momentul execuției și nu are un cuvânt cheie, de exemplu
boolean pentru declararea tipului (ca în alte limbaje, de exemplu Java) .
În Python, din perspectiva POO, tipul boolean este o subclasă a clasei întregilor. Variabila
booleană memorează la adresa unde a fost declarată, valoarea 1 pentru ‘True’ și 0 pentru False.
Comparațiile oricărei expresii cu True sau False se vor face prin operatorul de identitate is,
fie prin operatorul de egalitate ==.
int(True) == 1
int(False) == 0
a = [5,10,15,20,25,30,35,40]
# a[2] = 15
print("a[2] = ", a[2])
# a[0:3] = [5, 10, 15]
print("a[0:3] = ", a[0:3])
# a[5:] = [30, 35, 40]
print("a[5:] = ", a[5:])
Output
a[2] = 15
a[0:3] = [5, 10, 15]
a[5:] = [30, 35, 40]
Listele sunt mutabile, adică pot fi modificate.
Exemplul 1.16 #listele sunt mutabile
a = [1, 2, 3]
a[2] = 4
print(a)
Output
[1, 2, 4]
Definiție. Tuplul este o secvență ordonată de elemente, la fel ca o listă. Singura diferență este
ca tuplul este imutabil. Un tuplu odată creat, nu mai poate fi modificat.
Tuplurile sunt utilizate pentru protejarea datelor la scriere și sunt de obicei mai rapide decât
listele deoarece sunt mai simple, lipsindu-le unele elemente care le-ar permite să fie modificate
dinamic.
Un tuplu este delimitat de paranteze rotunde (), iar elementele sale sunt separate prin virgule.
xt = (5,'program', 1+3j)
Se poate utiliza operatorul de secvențiere/feliere/slicing [] pentru a extrage elementele, dar
valorile acestora nu se pot schimba.
Exemplul 1.17 #extragere elemente din tuplu
xt = (5,'program', 1+3j)
print("xt[1] = ", xt[1])
print("xt[0:3] = ", xt[0:3])
# Generare eroare, tuplurile sunt imutabile
xt[0] = 10
Output
xt[1] = program
xt[0:3] = (5, 'program', (1+3j))
xt[0] = 10
TypeError: 'tuple' object does not support item assignment
În Python, șirul (string) este o secvență de caractere Unicode. Reprezentarea șirurilor se face
cu ajutorul ghilimelelor simple sau duble. Șirurile multi-linie sunt delimitate de ghilimele
simple (apostrofuri) triplate: ''', sau ghilimele duble triplate: """.
Exemplul 1.18 #șiruri
s = "Sir pe o linie"
print(s)
s = '''Sir
multilinie'''
print(s)
Output
Sir pe o linie
Sir
multiline
Operatorul de secvențiere/feliere/slicing [ ] se poate utiliza cu șiruri, ca și în cazul tuplelor.
Asemănător, șirurile sunt imutabile, astfel că apare eroare dacă se încearcă modificarea unor
porțiuni din ele.
Exemplul 1.19 #șirurile sunt imutabile
s = 'Buna ziua!'
# s[3] = 'a'
print("s[3] = ", s[3])
# s[5:9] = 'cccc'
print("s[5:9] = ", s[5:9])
# Generare eroare, sirurile sunt imutabile in Python
s[5] ='w'
Output
s[3] = a
s[5:9] = ziua
Traceback (most recent call last):
. . . . .
s[5] ='w'
TypeError: 'str' object does not support item assignment
Output
41 Elemente introductive în limbajul Python
a = {1, 2, 3, 4, 5}
<class 'set'>
Se pot realiza operații ca reuniunea, sau intersecția a două mulțimi. În interiorul unei mulțimi,
nu există (se elimină) elemente duplicate, elementele unei mulțimi sunt unice.
Exemplul 1.21 #eliminarea duplicatelor într-o mulțime
a = {1,2,2,3,3,3}
print(a)
Output
{1, 2, 3}
Observație. Deoarece un set este o colecție neordonată, nu are sens operația de indexare
aplicată unei mulțimi. În consecință, operatorul de secvențiere [] nu funcționează.
Exemplul 1.22 #indecșii nu operează în set
>>> a = {1,2,3}
>>> a[1]
Traceback (most recent call last):
File "<string>", line 301, in runcode
File "<interactive input>", line 1, in <module>
TypeError: 'set' object does not support indexing
1.4.7 Dicționare
Output
<class 'dict'>
d[1] = valoare
d['cheie'] = 2
Traceback (most recent call last):
…
print("d[2] = ", d[2]);
KeyError: 2
42 Elemente introductive în limbajul Python
Output
tip data nr_int: <class 'int'>
tip data nr_fl: <class 'float'>
Valoare nr_suma: 2139.55
tip data nr_suma: <class 'float'>>
În următorul exemplu se adăugă un șir la un întreg
Exemplul 1.26 #Adunarea unui șir (dată mai mare) cu un întreg (dată mai mică)
num_int = 123
num_sir = "456"
print("tip data num_int:",type(num_int))
print("tip data num_sir:",type(num_sir))
print(num_int+num_sir)
Output
tip data num_int: <class 'int'>
tip data num_sir: <class 'str'>
Traceback (most recent call last):
…
print(num_int+num_sir)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Comentariu. În sevența de program de mai sus s-au adunat două variabile num_int (întreg) și
num_sir (șir), dar s-a obținut eroare deoarece Python nu a reușit să facă conversie implicită
pentru această combinație de tipuri.
Totuși există o soluție și pentru acest caz prin intermediul conversiei explicite.
Conversia de tip explicită
În cazul conversiei explicite de tip, utilizatorul însuși face conversia tipului de dată al unui
obiect în tipul de dată dorit. Astfel, pentru a face conversia forțată de tip de dată se utilizează
funcții predefinite ca int(), float(), str(), etc.
Observație. Acest tip de conversie este denumit casting deoarece programatorul schimbă
(casts) tipul de dată al obiectului. Castingul de tip poate fi făcut printr-o funcție aplicată
expresiei.
Sintaxa:
<tip de data dorit>(expresie)
Output
tip data num_int: <class 'int'>
tip data num_sir: <class 'str'>
tip data num_sir dupa casting: <class 'int'>
Suma num_int si num_sir: 579
tip data sum: <class 'int'>
Observații
1. Conversia de tip este conversia unui obiect de un anumit tip într-un alt obiect de tip diferit.
2. Conversia de tip implicită este făcută automat de către Python.
3. Python evită pierderea de date în conversia implicită prin conversia la tipul cel mai mare.
4. Conversia explicită a tipului mai este denumită și castingul de tip; utilizatorul folosește o
funcție pentru realizarea conversiei.
5. În castingul de tip, poate avea loc o pierdere de date dacă se forțează conversia la un tip
subimensionat.
Funcția print() se utilizează pentru afișarea datelor la ieșirea standard (ecran). Se mai poate
folosi și pentru scrierea de date într-un fișier.
Exemplul 1.29 #afișarea pe ecran cu print()
print('Afisare pe ecran')
Output
Afisare pe ecran
Output
Valoarea lui x este 7
În alt doilea exemplu se observă că la imprimare a fost adăugat un spațiu între șir și valoarea
variabilei x; acest lucru este implicit, dar acest lucru se poate modifica.
Sintaxa funcției print()este:
print(*objects, sep = ' ', end = '\n', file = sys.stdout, flush = False)
Output
1 2 3 4
1!2!3!4
1;2;3;4%
Formatarea ieșirii
Uneori se dorește atribuirirea unui aspect mai atractiv pentru ieșirea la imprimantă. Acest lucru
poate fi făcut prin utilizarea metodei str.format(), aplicabilă unui obiect de tip șir.
>>> x = 5; y = 10
>>> print('x = {} si y = {}'.format(x,y))
x = 5 si y = 10
Parantezele tip acoladă doar marchează poziția valorii imprimate, păstrând ordonarea
variabilelor din format. Se poate însă specifica ordinea apariției valorilor utilizând un index.
Exemplul 1.32 #print() cu formatare prin .format()
46 Elemente introductive în limbajul Python
Output
Prefer rosu și alb
Prefer alb și roșu
Se poate utiliza și stilul cu argumente pentru formatarea șirului.
Exemplul 1.33 #print() cu formatare prin utilizarea argumentelor
>>> print('Bună {salutul}, {nume}'.format(salutul = 'dimineața', nume =
'Popescu'))
Bună dimineața, Popescu
Sintaxa:
input([prompt])
unde prompt este șirul (opțional) dorit a se afișa pe ectran.
Exemplul 1.35 #introducere valoare de șir
>>>nr = input('Introduceti un numar: ')
Introduceti un numar: 25
>>> nr
'25'
Se observă că valoarea introdusă (25)este un șir, nu un număr. Pentru a converti acest șir într-
un număr, se poate folosi fie funcția int(), fie funcția float().
>>> int('25')
25
>>> float('25')
25.0
Output
3.141592653589793
Nu toate definițiile conținute în interiorul modulului math pot fi utile scopului propus în
programul realizat. În acest caz se pot importa doar elementele și funcțiile necesare, folosind
cuvântul cheie from. De exemplu:
>>> from math import pi
>>> pi
3.141592653589793
La importul unui modul, Python analizează mai multe locații, definite în sys.path.
>>> import sys
>>> sys.path
['C:\\Users\\USER',
'C:\\ProgramData\\Anaconda3\\python37.zip',
'C:\\ProgramData\\Anaconda3\\DLLs',
'C:\\ProgramData\\Anaconda3\\lib',
. . . . . . . . .
'C:\\ProgramData\\Anaconda3\\lib\\site-packages\\win32\\lib',
'C:\\ProgramData\\Anaconda3\\lib\\site-packages\\Pythonwin',
'C:\\ProgramData\\Anaconda3\\lib\\site-packages\\IPython\\extensions',
'C:\\Users\\USER\\.ipython']
. . . . . . . . . . . .
La această listă se pot adăuga și alte locații care interesează utilizatorul.
48 Elemente introductive în limbajul Python
Exemplul 1.38
x = 5
y = 3
print('x + y =',x+y)
print('x - y =',x-y)
print('x * y =',x*y)
print('x / y =',x/y)
print('x // y =',x//y)
print('x ** y =',x**y)
print('x % y =',x%y)
Output
x + y = 8
x - y = 2
x * y = 15
x / y = 1.6666666666666667
x // y = 1
x ** y = 125
x % y = 2
Output
x > y este: False
x < y este: True
x == y este: False
x != y este: True
x >= y este: False
x <= y este: True
Operatorii pe biți acționează asupra operanzilor ca și când aceștia ar fi șiruri de biți. Ei operează
bit cu bit, de aici numele lor.
De exemplu, în binar, 2 este reprezentat ca 10 și 4 este reprezentat ca 100.
În tabelul următor se prezintă operațiile posibile folosind operatorii pe biți, considerând x = 10
(0000 1010 în binar) și y = 4 (0000 0100 în binar)
Operator Semnificație Exemplu
& Bitwise AND x & y = 0 (0000 0000)
| Bitwise OR x | y = 14 (0000 1110)
~ Bitwise NOT ~x = -11 (1111 0101)
^ Bitwise XOR x ^ y = 14 (0000 1110)
>> Bitwise right shift x >> 2 = 2 (0000 0010)
<< Bitwise left shift x << 2 = 40 (0010 1000)
Operatorii de atribuire sunt utilizați în Python pentru a atribui valori unor variabile.
Operatorul simplu de atribuire
a=5 atribuie valoarea din dreapta (5) variabilei din stânga (a).
Operatorii compuși de atribuire
De asemenea, există mai mulți operatori compuși în Python, de exemplu a += 5 , care adună o
valoare la variabilă și apoi atribuie variabilei suma obținută. Acesta este echivalent cu a = a + 5.
Tabel cu operatorii compuși de atribuire
Operatorul Exemplu Echivalent cu
= x = 5 x = 5
+= x += 5 x = x + 5
-= x -= 5 x = x - 5
*= x *= 5 x = x * 5
/= x /= 5 x = x / 5
%= x %= 5 x = x % 5
//= x //= 5 x = x // 5
**= x **= 5 x = x ** 5
&= x &= 5 x = x & 5
|= x |= 5 x = x | 5
^= x ^= 5 x = x ^ 5
>>= x >>= 5 x = x >> 5
<<= x <<= 5 x = x << 5
51 Elemente introductive în limbajul Python
Limbajul Python oferă două tipuri speciale de operatori: operatorii de identitate și operatorii de
apartenență. Aceștia sunt descriși în continuare, cu exemple.
Operatorii de identitate
Operatorii de identitate în Python sunt is și is not . Ei sunt utilizați pentru a se verifica dacă
două valori (sau variabile) sunt localizate în aceeași memorie. Dacă două variabile sunt egale,
nu înseamnă că implicit sunt și identice.
Operator Semnificație Exemple
is Adevărat dacă operanzii sunt identici (se referă la
același obiect) x is True
is not Adevărat dacă operanzii nu sunt identici
(nu se referă la același obiect) x is not True
Output
False
True
False
Se vede că x1 și y1 sunt întregi de aceeași valoare, astfel că ei sunt atât egali, cât și identici.
Aceeași situație e valabilă și pentru x2 și y2 (șiruri).
x3 și y3 sunt liste. Ele sunt egale, dar nu identice, deoarece interpretorul Python le dispune
separat în memorie, deși ele sunt egale.
1.7.7 Operatorii de apartenență
Operatorii de apartenență în Python sunt in și not in. Ei sunt utilizați pentru a testa dacă o
valoare sau o variabilă se găsește conținută într-o secvență (șir, listă, tuplu, dicționar).
Observație. Într-un dicționar se poate testa doar prezența unei chei, nu a valorii sale.
Operator Semnificație Exemplu
In Adevărat dacă valoarea/variabila este
găsită în secvență 5 in x
not in Adevărat dacă valoarea/variabila nu este
găsită în secvență 5 not in x
print('a' in y)
Output
True
True
True
False
Se observă că 'D' este în x , dar 'drum' nu este present în x (de reamintit, Python este sensibil
la majuscule). Similar, 1 este cheie și 'a' este valoare în dictionarul y. Din acest motiv, expresia
'a' in y întoarce False.
Output
id(2) = 9302208
id(a) = 9302208
Aici, ambele referințe sunt la același obiect 2, astfel ca se obține același id(). Dacă se face o
mică modificare, se pot obține diferite valori pentru id().
Exemplul 1.44 #ilustrare funcția built-in id(), legarea dinamică
a = 2
print('id(a) =', id(a))
a = a+1
print('id(a) =', id(a))
print('id(3) =', id(3))
b = 2
print('id(b) =', id(b))
print('id(2) =', id(2))
Output
id(a) = 9302208
id(a) = 9302240
id(3) = 9302240
id(b) = 9302208
id(2) = 9302208
- În continuare, când b = 2 este executat, noul nume b devine asociat cu obiectul anterior 2.
Observație. Acest mod de lucru este eficient deoarece Python nu mai trebuie să creeze un nou
duplicat al obiectului. Această legare dinamică a numelor (name binding) face puternic limbajul
Python. Un nume poate fi referință la orice tip de obiect.
Exemplul 1.45 #ilustrare funcția built-in id(), redenumirea obiectelor
>>> a = 5
>>> id(a)
140711384218720
>>> a = 'Hello World!'
>>> id(a)
1677728483568
>>> a = [1,2,3]
>>> id(a)
1677728522523
Toate cele trei linii de cod sunt valide, iar a se referă la trei tipuri de obiecte diferite în trei
cazuri diferite, create succesiv.
Funcțiile sunt de asemenea obiecte, astfel că un nume poate de asemenea să facă referință și la
ele.
Exemplul 1.46 #ilustrare funcția built-in id()aplicată la numele funcțiilor
def Salut():
print("Salut!")
a = Salut
a()
id(a)
Output
Salut!
140711383762960
În codul de mai sus, numele a face referire la o funcție. Funcția poate fi apelată apoi utilizând
numele a.
Built-in
Într-un program se pot defini diferite spații ale numelor unice. Acestea nu pot fi însă accesate
toate din oricare parte a programului.
Un domeniu de vizibilitate (scope) este o parte a programului din care un spațiu de nume
(namespace) poate fi accesat direct fără a folosi vreun prefix.
În orice moment, sunt disponibile cel puțin trei domenii de vizibilitate încuibate.
1. Domeniul de vizibilitate al funcției curente - conținând numele locale.
2. Domeniul de vizibilitate al modulului - care conține nume globale.
3. Domeniul de vizibilitate cel mai cuprinzător - care conține numele preconstruite (built-in
names).
Dacă se face o referință printr-un nume în interiorul unei funcții, atunci numele este căutat mai
întâi în spațiul de nume local, apoi în spațiul de nume global și în final în spațiul de nume built-
in (care conține numele funcțiilor preconstruite).
Dacă o funcție se utilizează în interiorul unei funcții, atunci un nou domeniu de vizibilitate
(scope) este încuibat în domeniul de vizibilitate local.
Exemplul 1.47 #ilustrare scope și namespace
a = 11
def functie():
b = 33
def functie_interna():
c = 55
În exemplul de mai sus, variabila a este în spațiul de nume global. Variabila b este în spațiul de
nume local al funcției functie(), iar c este în spațiul de nume local, încuibat, al funcției
functie_interna().
Output
a = 30
a = 15
a = 5
În programul de mai sus, trei variabile diferite a sunt definite în spații de nume separate, și
accesate în fiecare spațiu.
În programul următor, toate referințele și atribuirile se fac la variabila globală a, datorită
utilizării cuvântului cheie global.
Exemplul 1.49 # a este definită ca variabilă globală în functia_internă()
def functia():
global a
a = 20
def functia_interna():
global a
a = 30
print('a =', a)
functia_interna()
print('a =', a)
a = 10
functia()
print('a =', a)
Output
a = 30
a = 30
a = 30
>>>xlista
[9, 4, 5, 3, 6, 1]
Tuplele și șirurile sunt exemple de date imutabile. Conținutul lor nu poate fi modificat după
ce au fost create.
>>>xtuplu = (2, 5, 3, 1)
>>>xtuplu[0] = 9
Traceback (most recent call last):
. . . . . .
xtuplu[0] = 9
TypeError: 'tuple' object does not support item assignment
În legătură cu mutabilitatea există aspectul denumit aliasing. Un alias este un alt nume de
variabilă pentru aceeași dată. În acest caz, modificând valoarea unui nume se modifică și
valoarea celuilalt nume (aliasul).
Exemplul 1.50 # numele alias - proprietăți
>>>xlista_1 = [1, 2, 3, 4, 6]
>>>xlista_2 = xlista_1 #atribuire alias (alt nume)
>>>xlista_2[-1] = 5
>>>xlista_1
[1, 2, 3, 4, 5]
Acest lucru se întâmplă deoarece atât xlista_1 cât și xlista_2 referă aceeași adresă de
locație de memorie.
>>>xlista_1 = [1, 2, 3, 4, 6]
>>>xlista_2 = xlista_1
>>>id(xlista_1) == id(xlista_2)
True
Se poate depăși această problemă prin utilizarea funcției de copiere copy, astfel:
>>>xlista_1 = [1, 2, 3, 4, 6]
>>>xlista_2 = xlista_1[:] # copiere !
>>>id(xlista_1) == id(xlista_2)
False
>>>xlista_2[-1] = 5
>>>xlista_2
[1, 2, 3, 4, 5]
>>>xlista_1 #modificarea din xlista_2 nu a modificat xlista_1
[1, 2, 3, 4, 6]
R1.10.2 b
57 Elemente introductive în limbajul Python
R1.10.5. a și b
R1.10.6 b
R1.10.9 a
1.1010 Care este sintaxa corectă pentru a afișa "Salut " :
a. print " Salut!" b. echo "Salut!" c. print("Salut!") d. p("Salut!")
R1.10.10 c
1.10.11 Care este sintaxa corectă pentru afișarea tipului unei variable în Python ?
a. print(type(x)) b. print(typeof x) c. print(typeof(x)) d. print(type x)
R1.10.11 c
R1.10.12 a
n1 = -5
n2 = 2.5
print(n1//n2)
a) -3.0 b) -2 c} -2.0 d) 1.0
R1.10.13 c
a) adevărată b) falsă
R1.10.14 a
CAPITOLUL 2
Condiția c este evaluată prima. Dacă rezultatul este True, se evaluează x și se întoarce valoarea
lui x, iar dacă rezultatul este False, se evaluează y și se întoarce valoarea lui y.
Expresia condițională poate fi scrisă utilizând operatori aritmetici și logici.
Observație. Se reamintește că în limbajul C/C++ (și în alte limbaje) există expresia
condițională de forma:
z = c ? x : y
Output 1
Numarul = 5
este impar
Output 2
Numarul = 6
este par
a) if …
b) if … else
c) if … elif … else
d) if … elif … elif … … else
a) Sintaxa instrucțiunii if …
if test_expresie:
instrucțiuni Fig. 2.1 Diagrama logică a instrucțiunii
if în Python
La execuție, programul evaluează test_expresie și va executa instrucțiunile doar dacă
expresia testată este adevărată (True), figura 2.1.
Dacă expresia testată este falsă (False), instrucțiunile nu sunt executate.
În Python, corpul instrucțiunii if este indicat prin indentare. Începutul corpului este marcat de
o indentare, iar sfârșitul său este delimitat de prima linie neindentată care îi urmează, sau cu
indentare diferită.
Observație. Python interpretează valorile diferite de zero ca True, iar valorile 0 și None ca
False.
num = 3
if num > 0:
print(num, "numărul este pozitiv")
print("Mesaj dupa sfarsit if)")
num = -1
if num > 0:
print(num, " numărul este pozitiv")
print("Mesaj de asemenea dupa sfarsit if")
Output
61 Controlul fluxului de prelucrare
if expresie_test:
corpul lui if
else:
corpul lui else
if num >= 0:
print("Pozitiv sau zero")
else:
print("Număr negativ")
num = -3
if num >= 0:
print("Pozitiv sau zero")
else:
print("Număr negativ")
Output
Pozitiv sau zero
Număr negativ
62 Controlul fluxului de prelucrare
În exemplu de mai sus, când num este egal cu 3, expresia testată este True și corpul lui if
este executat, iar corpul lui else este sărit.
Dacă num este egal cu -3, expresia testată este False, corpul lui if este sărit, iar corpul lui
else executat.
Dacă num este egal cu 0, expresia testată este True și corpul lui if este executat, iar corpul
lui else este sărit.
if expresie_test1:
Corpul lui if
elif expresie_test2:
Corpul lui elif
else:
Corpul lui else
În funcție de condiție, doar un singur bloc dintre mai multe blocuri aparținând instrucțiunii
if...elif...else este executat.
Blocul if poate avea un singur bloc else, dar poate avea mai multe blocuri elif.
num = 3.4
if num > 0:
print("Numar pozitiv")
elif num == 0:
print("Zero")
else:
print("Numar negativ")
num = -4.5
if num > 0:
print("Numar pozitiv")
63 Controlul fluxului de prelucrare
elif num == 0:
print("Zero")
else:
print("Numar negativ")
num = 0
if num > 0:
print("Numar pozitiv")
elif num == 0:
print("Zero")
else:
print("Numar negativ")
Output
Numar pozitiv
Numar negativ
Zero
În interiorul unei structuri de tipul if...elif...else pot exista mai multe clause elif.
Observație. Posibilitatea existenței mai multor clause elif este motivul pentru care Python
nu conține structura switch-case, pentru selecții multiple, întâlnită în alte limbaje. Se presupune
că structura switch-case se poate simula prin instrucțiunea if cu elif multiplu.
if expresie_1:
instrucțiune/instrucțiuni
elif expresie_2:
instrucțiune/instrucțiuni
. . . . . . . . . .
elif expresie_n:
instrucțiune/instrucțiuni
else:
instrucțiune/instrucțiuni
Instrucțiunea if încuibată
Poate exista o
instrucțiune if...elif...else în interiorul altei instrucțiuni
if...elif...else. Această structură este denumită încuibare (nesting) în limbajele de
programare.
Orice număr de instrucțiuni sau niveluri de încuibare pot fi încuibate. Singurul mod de marcare
a nivelului este utilizarea indentării. Deoarece acest lucru poate produce confuzie, este
recomandată evitarea incuibării, dacă nu este absolut necesară.
Output 1
Introduceti un numar: 5
Număr Pozitiv
Output 2
Introduceti un numar: -1
Număr negativ
Output 3
Introduceti un număr: 0
Zero
Output
Inaltimea in centimetri = 170
Greutatea in Kg = 80
Indicele de masa corporala este = 27.68166089965398
Moderat supraponderal
IMC= 27.68166089965398
În structura de calcul de mai sus, clauza case constă dintr-un șablon/pattern (pattern_1,
pattern_2, etc.) care va fi potrivit prin comparație cu valoarea variabilei nume_variabila. În
cazul în care valoarea de adevăr a potrivirii este True pentru una din clauze, se va executa
blocul de instrucțiuni corespunzător acelei clauze.
Potrivirea 2
Output
Suma este = 48
Funcția range()
Se poate genera o secvență de numere utilizând funcția range().
range(10) va genera numere de la 0 to 9 (10 numere).
Se pot defini argumentele start, stop și mărime pas, astfel:
range(start, stop, mărime pas).
Pentru a forța această funcție să producă toate valorile la ieșire se poate utiliza funcția list().
Exemplul 2.9 #ilustrarea funcționării funcției range()
print(range(10))
print(list(range(10)))
67 Controlul fluxului de prelucrare
print(list(range(2, 8)))
print(list(range(2, 20, 3)))
Output
range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[2, 3, 4, 5, 6, 7]
[2, 5, 8, 11, 14, 17]
Se poate utiliza funcția range() în buclele for pentru a itera o secvență de numere. Aceasta
se poate combina cu funcția len() pentru iterarea secvenței prin indexare.
Output
Gen muzica : 0 pop
Gen muzica : 1 rock
Gen muzica : 2 jazz
Output
0
1
5
Nu mai sunt elemente.
Exemplul 2.12 #execuția blocului else se face doar dacă nu a fost executat break
# notele unui student sunt afișate doar daca
#numele studentului exista in catalog
catalog = {'Ion': 9, 'Alex': 5, 'George': 7}
student_name = 'Mihai'
for student in catalog:
68 Controlul fluxului de prelucrare
if student == student_nume:
print(catalog[student])
break
else:
print('Numele nu a fost gasit')
Output
'Numele nu a fost gasit'
print(RomanDecimal('XI'))
Output
11
while expresie_test:
Corpul lui while
În bucla while se testează mai întâi expresia, urmând să se execute corpul de instrucțiuni care
urmează doar dacă expresie_test a fost evaluată ca fiind True. După o iterație,
69 Controlul fluxului de prelucrare
expresie_test este evaluată din nou. Procesul iterare-verificare continuă până cînd
expresie_test devine False, figura 2.5.
În Python, corpul buclei while este delimitat prin indentare.
Corpul începe cu indentarea și se termină la prima linie neindentată.
Python interpretează orice valoare non-zero ca fiind True, iar None și 0 sunt interpretate ca
False.
#Însumarea primelor n
#numere naturale
#suma = 1+2+3+...+n
#Utilizatorul introduce n
n = int(input("n = "))
suma = 0
i = 1
while i <= n:
suma = suma + i
i = i+1
# Afișează suma
print("Suma este = ", suma)
Output
n = 10
Suma este 55 Fig. 2.5 Funcționarea buclei while
În secvența program de mai sus, expresia va fi True atât timp cât variabila contor i este mai
mica sau egală cu n (10 în exemplu)
În corpul buclei while este necesar să se crească valoarea variabilei contor, altfel se obține o
buclă infinită.
Ca și în cazul buclei for, bucla while poate avea un bloc else optional.
Blocul else este executat în momentul în care condiția din bucla while este evaluată ca fiind
False.
Bucla while se poate termina cu o instrucțiune break. În acest caz, blocul else este ignorat.
În consecință, blocul else se execută doar dacă condiția este falsă și dacă nu apare un break.
Observație. Regulile de bună practică în programarea Python recomandă totuși evitarea
clauzei else în bucla while, dacă nu e strict necesar, din diverse motive.
Exemplul 2.15 #bucla while cu else
'''Utilizarea lui else
cu bucla while'''
contor = 0
while contor < 3:
print("Interior bucla")
contor = contor + 1
else:
70 Controlul fluxului de prelucrare
print("Interior else")
Output
Interior bucla
Interior bucla
Interior bucla
Interior else
La a patra iterație, condiția buclei while devine False. În consecință, blocul else este
executat.
Modul de lucru al instrucțiunii break în bucla for și în bucla while este prezentat mai jos.
A. break în bucla for:
Output
i
n
s
Terminat
În secvența program de mai sus se parcurge șirul "instructiune". Se verifică apariția literei
t, iar când se confirmă se părăsește cu break bucla. În consecință, se afișează toate literele
până la t, după care bucla se încheie.
Înstrucțiunea continue este utilizată pentru a sări restul codului în cadrul buclei, doar în
cursul iterației curente. Bucla repetitivă nu se încheie, dar se reia începând cu iterația
următoare.
Sintaxa
continue
Modul de lucru al instrucțiunii continue în buclele for și while este prezentat mai jos.
C. continue în bucla for:
print("Sfarsit")
Output
p
r
o
r
a
m
Sfarsit
Având instrucțiunea continue conținută în buclă, la întâlnirea caracterului g în șir, restul buclei
nu se mai execută. Se observă în exemplul de mai sus că toate literele, exceptând g, sunt
imprimate.
# A. True
# B. Error
73 Controlul fluxului de prelucrare
# C. False
# D. None
R2.9.1 Răspuns: C
2.9.2 Care va fi ieșirea secvenței:
for num in range(5):
pass
print(num)
A. 5
B. 4
C. Error
D. None
R2.9.4 Raspuns: D
2.9.5 Să se scrie o secvență de program pentru exemplificarea utilizării wildcardului “_” cu
instrucțiunea match-case.
R2.9.5
buton5 = 7 #valoare non-booleană
#buton5 = bool(7) #valoare booleană
match buton5:
case (True):
print("Sistemul este pornit")
exit()
case (False):
74 Controlul fluxului de prelucrare
Output
Sistemul nu a fost inițializat
2.9.6 Să se scrie o secvență de program pentru exemplificarea utilizării paternului multiplu
obținut cu operatorul logic OR / ( | ) , utilizând instrucțiunea match-case.
R2.9.6
VarTest = True
match VarTest:
case (True|False):
print("VarTest conține o valoare booleană")
case _ :
print("VarTest nu conține o valoare booleană")
Output
VarTest conține o valoare booleană
2.9.7 Să se scrie o secvență de program pentru verificarea unei colecții de valori, utilizând
instrucțiunea match-case.
R2.9.7
lista1 = ['a', 'b', 'c', 'd']
match lista1:
case ['e','f'] : print("e,f nu exista")
case ['a','b','c','d'] : print("a,b,c,d prezente")
Output
a,b,c,d prezente
print("Numar negativ")
case n if n == 0:
print("Numar egal cu zero")
case n if n > 0:
print("Numar pozitiv")
Output
Numar negativ
2.9.10 Să se scrie o secvență de program utilizând for care să imprime o piramidă.
R2.9.10 Se scrie și se rulează codul:
def piramida(n):
k = 2*n - 2
for i in range (0, n):
for j in range(0, k):
print(end=" ")
k = k - 1
for j in range(0, i+1):
print("*", end = " ")
print("")
piramida(5)
Output
*
* *
* * *
* * * *
* * * * *
2.9.11 Să se scrie o secvență de program utilizând for care să imprime o piramidă inversată.
R2.9.11
def pattern_inv(n):
k = 2*n - 2
for i in range (n, -1, -1):
for j in range(k, 0, -1):
print(end=" ")
k = k + 1
for j in range(0, i+1):
print("*", end=" ")
print("")
pattern_inv(5)
Output
* * * * * *
* * * * *
* * * *
* * *
* *
*
2.9.12 Să se scrie o secvență de program utilizând for cu enumerate care să imprime lista și
să afișeze numărul de ordine al elementelor:
Lista_fructe = ["Cireasa", "Para", "Pruna", "Gutuie"]
76 Controlul fluxului de prelucrare
R2.9.12
Lista_fructe = ["Cireasa", "Para", "Pruna", "Gutuie"]
for numar, fruct in enumerate(Lista_fructe):
print(numar, fruct)
Output
0 Cireasa
1 Para
2 Pruna
3 Gutuie
Output
1
12
123
1234
12345
2.9.14 Cum se poate obține din intrarea dată mai jos, ieșirea care urmează:
input=[10,20,30,40,50,60,70,80,90,100]
output=[10,30,60,100]
R2.9.14
input = [x for x in range(10, 101, 10)]
output = []
y=0
i=2
while y < len(input):
output.append(input[y])
y=y+i
i=i+1
print(output)
Output
[10, 30, 60, 100]
2.8.15 Să se scrie o secvență de program folosind if…elif…else care să precizeze că un
număr este pozitiv par sau impar, sau zero, sau negativ.
R2.9.15
77 Controlul fluxului de prelucrare
x = int(input('prompt='))
if x > 0:
print("numar pozitiv")
if x%2 == 0:
print('numar par')
else:
print('numar impar')
elif x<0:
print('numar negativ')
else:
print('numarul este zero')
Eugen Diaconescu Limbajul și ecosistemul de programare Python
CAPITOLUL 3
Definiție. În Python, funcția este un grup de instrucțiuni, având un nume, care îndeplinește o
acțiune determinată.
Funcțiile ajută la împărțirea unui program în părți mai mici, mai ușor de administrat. Pe măsură
ce un program crește, se observă mai bine avantajele modularizării prin funcții, deoarece
programul poate fi mai ușor organizat și controlat.
În plus, funcțiile permit evitarea repetițiilor și fac codul reutilizabil.
Sintaxa
def nume_functie(parametri):
"""șir documentar (docstring)"""
instrucțiuni
Tipuri de funcții
În Python, funcțiile pot fi clasificate în două categorii:
1. Funcții preconstruite (built-in).
2. Funcții definite de utilizator.
3.1.2 Docstringuri
Primul șir situat după antetul funcției se numește “docstring”, rolul său fiind să explice ce face
funcția. De notat că, deși este opțională, documentarea programului este în practică foarte
folositoare, fiind utilă pentru reamintirea detaliilor de realizare și funcționare ale codului.
Utilizând ghilimele simple sau duble, triplate, așa cum s-a văzut și în exemplul de mai sus,
docstringul poate fi extins la mai multe linii. Docstringul este disponibil programatorului ca
atribut al funcției având numele __doc__ . Atributul mes_vaccin.__doc__ se poate imprima
la consola Python.
Exemplul 3.2 #afișarea docstringului multilinie
>>> print(mes_vaccin.__doc__)
Output
Această funcție reamintește unei persoane
ziua cind este programată la vaccinare. Numele său
este comunicat sub forma de parametru funcției
Instrucțiunea return poate întoarce o expresie sau niciuna (întoarce obiectul None). Expresia
întoarsă reprezintă valoarea sau lista de valori rezultate ca urmare a evaluărilor în cadrul
funcției.
Exemplul 3.3 #Apel funcție fără return.
>>> print(mes_vaccin("Ionescu B."))
Output
Domnule/Doamnă Ionescu B., luni sunteți programat/ă la vaccinare!
None
return nr
else:
return -nr
print(val_absoluta(5))
print(val_absoluta(-7))
Output
5
7
Definiție. Domeniul de vizibilitate (scope) este partea programului unde variabila este
recunoscută.
Parametrii și variabilele definite în cadrul funcției nu sunt vizibile din exteriorul funcției. În
acest caz, domeniul de vizibilitate este local.
Definiție. Durata de viață a unei variabile este (coincide cu) perioada în care funcția există în
memorie. Dacă memoria este dealocată, sau este rescrisă, variabila dispare. În consecință,
durata sau timpul de viață (lifetime) există doar cât timp se execută funcția.
Variabilele locale sunt distruse în momentul în care are loc reîntoarcerea din funcție. Altfel
spus, funcția nu-și reamintește valorile variabilelor din apelurile sale anterioare.
Exemplul 3.5 #Domeniul de vizibilitate este interiorul funcției
x = 20
def xfunc():
x = 10
print("Valoarea în interiorul functiei=",x)
xfunc()
print("Valoarea în exteriorul functiei=",x)
Output
Valoarea în interiorul functiei= 10
Valoarea în exteriorul functiei= 20
În programul de mai sus, valoarea inițială a lui x este 20. Chiar dacă functia xfunc() a schimbat
valoarea lui x în 10, nu este afectat x în afara funcției. Deși apare același nume x, există de fapt
două variabile, una locală funcției, iar cealaltă, externă ei.
Pe de altă parte, variabilele externe funcției sunt vizibile în interior. Se spune că acestea au un
domeniu de vizibilitate global. Variabilele globale pot fi vizibile în interiorul unei funcții, dar
nu pot fi modificate decât dacă li se adaugă cuvântul cheie global.
Definiție. Parametrii transmiși funcțiilor, în număr variabil, sub forma conținutului unei liste
plasate în antetul funcției, sunt denumiți argumentele funcției.
3.1.5.1 Funcții cu număr fix de argumente
Se observă că folosind funcția care urmează, de tipul “definită de utilizator”, dacă se apelează
folosind același număr de parametri egal cu cel folosit la definiție, nu se obține nici o eroare.
81 Funcții, module și pachete
Output
Domnule/Doamnă Georgescu M., joi sunteți programat/ă la vaccinare!
Observație. Funcția mes_vaccin() are două argumente. Dacă se apelează folosind un număr
diferit de argumente, se va obține o eroare de la interpretor.
>>> mes_vaccin("Georgescu M.")
mes_vaccin() missing 1 required positional argument: 'zi'
>>> mes_vaccin()
mes_vaccin() missing 2 required positional arguments: 'nume' and 'zi'
Output
Domnule/Doamnă Georgescu M., joi sunteți programat/ă la vaccinare!
Domnule/Doamnă Georgescu M., vineri sunteți programat/ă la vaccinare!
În această funcție, parametrul nume nu are o valoare implicită și în consecință este obligatoriu
să fie folosit în antetul funcției, la apelare.
Pe de altă parte, parametrul zi are valoarea implicită "vineri" și drept urmare poate fi optional
în timpul apelului. De asemenea, valoarea implicită poate fi suprascrisă.
Numărul de argumente din antetul funcției care pot avea valori implicite nu este restricționat.
Observație. Odată ce un argument are valoare implicită, toate argumentele de la dreapta sa
trebuie să aibă valori implicite. Acest lucru impune ca, la definiție, argumentele ne-inițializate
nu pot să urmeze după argumentele cu valori inițializate (implicite).
82 Funcții, module și pachete
Se va genera o eroare:
SyntaxError: non-default argument follows default argument
Când se apelează o funcție folosind parametri efectivi (adică valori), aceștia se atribuie
argumentelor corespunzător poziției lor.
De exemplu, folosind funcția de mai sus, mes_vaccin(), când este apelată ca
mes_vaccin("Georgescu M.", "joi"), valoarea " Georgescu M." este atribuită
argumentului nume și similar valoarea "joi" argumentului zi.
Observație. Python permite funcțiilor să fie apelate utilizând și numele argumentelor. Când se
apelează funcțiile în acest mod, ordinea argumentelor poate fi schimbată, ca în exemplul care
urmează.
Exemplul 3.8 #ordinea argumentelor, chiar inițializate, schimbată la apel
def mes_vaccin(nume, zi = "vineri"):
"""
Această funcție reamintește unei persoane
ziua cind este programată la vaccinare. Numele său
este comunicat sub forma de parametru funcției
"""
print("Domnule/Doamnă " + nume + ", " + zi + " sunteți programat/ă la
vaccinare!")
Va apărea eroarea:
SyntaxError: positional argument follows keyword argument
Output
Culoare preferata: alb
Culoare preferata: rosu
Culoare preferata: verde
Culoare preferata: galben
Culoare preferata: albastru
În acest exemplu, s-a apelat funcția utilizându-se mai multe argumente. Argumentele au fost
grupate într-un tuplu înainte de a fi trecute funcției. În interiorul funcției se utilizează o buclă
for pentru a se recepționa toate argumentele transmise la momentul apelului.
3.1.6 Recursivitatea
Definiție. Recursivitatea este un process de definire a unei entități prin aceasta însăși. O funcție
recursivă este o funcție care se apelează pe ea însăși.
Avantajele recursivității
1. Funcțiile recursive fac programul mai clar și mai elegant.
2. Un process complex poate fi divizat în subprograme mai simple prin utilizarea recursivității.
3. Codul generat este mai ușor de înțeles decât dacă se utilizează iterații încuibate.
Dezavantajele recursivității
1. Sunt cazuri când logica din spatele recursivității este mai greu de urmărit.
2. Apelurile recursive consumă mai multe resurse de memorie și timp de execuție.
3. Funcțiile recursive sunt mai dificil de depanat.
Exemplul 3.10 #ilustrarea recursivității la calculul factorialului
#Funcția factorial face produsul tuturor numerelor
#de la 1 până la acel întreg. De exemplu, factorial de 5 (notat 5!) este
#1*2*3*4*5 = 120.
def factorial(x):
"""Functia calculeaza
factorialul unui numar intreg"""
if x == 1:
return 1
else:
return (x * factorial(x-1))
nr = 3
print("Factorialul lui ", nr, "este", factorial(nr))
Output
Factorialul lui 3 este 6
84 Funcții, module și pachete
Când se execută funcția factorial() de mai sus, cu întregul 3, ea se apelează pe sine insăși
(recursiv) prin descreșterea numărului întreg dat ca argument, până ce acesta devine egal cu 1,
astfel:
factorial(3) # primul apel cu 3
3 * factorial(2) # al doilea apel cu 2
3 * 2 * factorial(1) # al treilea apel cu 1
3 * 2 * 1 # return din al treilea apel
3 * 2 # return din al doilea apel
6 # return din primul apel
De notat că orice funcție recursivă trebuie să aibă o condiție de oprire a apelurilor recursive,
altfel ea ar putea să se autoapeleze de un număr infinit de ori. În exemplul de mai sus, condiția
de oprire este n = 1.
Interpretorul Python limitează totuși adâncimea recursiunii la 1000, pentru a nu se depăși stiva
implicată. Dacă limita este depășită, apare RecursionError.
Funcțiile lambda pot avea oricâte argumente, dar reunite într-o singură expresie, care este
evaluată și returnată.
Exemplul 3.11 #definirea unei funcții lambda
# Dublarea valorii variabilei, utilizând o funcție lambda
putere2 = lambda x: x**2
p2 = lambda x: x**2
print(putere2(7))
print(p2(9))
Output
49
81
În exemplul de mai sus, funcția lambda este: lambda x: x*2 , unde x este argumentul și x**2
este expresia ce trebuie evaluate și returnată. Funcția nu are nume, totuși întoarce un obiect
funcție căruia îi sunt atribuite numele putere2, respectiv p2. Cu aceste nume se poate face
un apel ca și cu o funcție normală.
Instrucțiunea:
putere2 = lambda x: x * 2
Output
[12, 14, 78, 18]
Exemplul 3.13
"""Funcția map()poate avea ca argumente o funcție (lambda) și o listă.
Funcția este apelată având argumente o listă inițială și funcția lambda și
produce o listă nouă obținută prin utilizarea funcției lambda. Lista
rezultată conține elementele returnate de funcția lambda pentru fiecare
element al listei inițiale. """
# Dublarea elementelor unei liste folosind funcția map()
xlista = [12, 57, 14, 61, 78, 11, 18, 21]
ylista = list(map(lambda x: x*2 , xlista))
print(ylista)
Output
[24, 114, 28, 122, 156, 22, 36, 42]
Definiție. În Python, o variabilă declarată în afara funcției sau cu vizibilitate globală se numește
variabilă globală. Aceasta poate fi accesată fie în interiorul funcției, fie în exteriorul ei.
Exemplul 3.14 #variabila globală
#Crearea unei variabile globale
x = "var_global"
def test():
print("x in interior:", x)
test()
print("x in exterior:", x)
Output
x in interior: var_global
x in exterior: var_global
86 Funcții, module și pachete
În secvența de program de mai sus, s-a creat o variabilă globală denumită x și s-a definit o
funcție test() pentru imprimarea variabilei globale x. La sfârșit s-a apelat funcția test() care
a apelat valoarea lui x.
Dacă se dorește modificarea valorii lui x în interiorul funcției se va obține o eroare, ca în
exemplul următor:
Exemplul 3.15 #eroare la modificarea unei variabile globale
x = " var_global"
def test():
x = 5 * x
print(x)
test()
Output
Eroare apare datorită faptului că Python tratează x ca o variabilă locală în interiorul funcției
test(), dar x nu este definit în interiorul acestei funcții (nu are atribuită o valoare).
Pentru a reuși totuși ce s-a propus, este necesară utilizarea cuvântului cheie global.
Observație. Cuvântul cheie global este necesar pentru variabilele folosite cu același nume în
exteriorul și interiorul funcției. În cazul exemplului de mai sus, dacă nu este necesară
memorarea modificării suportate în cazul funcției, lucrurile se rezolvă simplu, utilizând x doar
ca variabilă locală:
x = " var_global"
def test(x):
x = 5 * x
print(x)
test(x)
Output
var_global var_global var_global var_global var_global
Dar nu acest lucru se dorește! În acest context, x din interiorul funcției nu mai este variabilă
globală, ci locală!
Observație. Variabila globală poate fi modificată în interiorul unei funcții doar dacă este
mutabilă (de exemplu, o variabilă numerică, o listă, etc.). Variabilele imutabile nu pot fi
modificate, ci doar citite (de exemplu, un tuplu).
test()
Output
Local
Variabila z se poate accesa și eventual modifica deoarece acest lucru se face din interiorul
spațiului său de vizibilitate (comanda print()se află în interiorul funcției test()).
Exemplul 3.17 #accesarea unei variabile locale din exteriorul funcției
#Accesarea unei variabile locale din afara domeniului de vizibilitate
def test():
z = "local"
test()
print(z)
Output
NameError: name 'z' is not defined
La ieșire apare eroare deoarece se încearcă accesarea variabilei locale z din exteriorul funcției,
variabila z existând doar în interiorul funcției test(), unde are o vizibilitate locală.
În același program, se pot utiliza simultan variabile locale și globale, după cerințele
algoritmului.
Examplul 3.18 #Variabile locale și globale în același program
x = "global"
def test():
global x
y = "local"
x = x*3 #multiplicarea continutului variabilei globale x
print(x)
print(y)
test()
Output
globalglobalglobal
local
În secvența de program de mai sus, s-a declarat x ca variabilă globală și y ca variabilă locală în
funcția test(). Apoi s-a utilizat operatorul de multiplicare * pentru a modifica variabila
globală x și s-au imprimat atât x cât și y.
După apelarea funcției test(), valoarea lui x devine globalglobalglobal deoarece s-a
utilizat x*3 pentru a imprima textul multiplicat global. După aceasta, s-a imprimat valoarea
variabilei locale y ca local.
Exemplul 3.19 #Variabila globală și variabila locală au același nume
x = 5
def test():
x = 10
print("local x:", x)
test()
print("global x:", x)
88 Funcții, module și pachete
Output
local x: 10
global x: 5
În secvența de program de mai sus, s-a utilizat același nume x atât pentru variabila globală cât
și cea locală. Se obține un rezultat diferit la imprimarea variabilelor deoarece variabila este
declarată în două domenii de vizibilitate, domeniul local de vizibilitate în interiorul funcției
test() și domeniul global de vizibilitate în exteriorul funcției test().
Când se imprimă variabila în interiorul funcției test() se afișează valoarea local x: 10.
Acesta este domeniul de vizibilitate local al variabilei.
Asemănător, când se imprimă variabila în exteriorul funcției test(), se afișează global x:
5. Acesta este domeniul de vizibilitate global al variabilei.
Output
interior: nonlocal
exterior: nonlocal
În secvența de program de mai sus se află funcția încuibată interior(). Se utilizează cuvântul
cheie nonlocal pentru a crea o variabilă nelocală. Funcția interior() este definită în
domeniul de vizibilitate al funcției exterior().
Observație: Dacă se schimbă valoarea unei variabile nelocale, schimbările apar în variabila
locală.
3.1.12 Utilizarea cuvântului cheie global
În Python, cuvântul cheie global permite modificarea variabilei în afara domeniului de
vizibilitate current. El se utilizează pentru a crea o variabilă globală și apoi pentru a permite
efectuarea de modificări ale variabilei într-un context local.
Regulile de bază pentru cuvântul cheie global în Python sunt:
• Când se creează o variabilă în interiorul unei funcții, aceasta este implicit locală.
89 Funcții, module și pachete
• Când se definește o variabilă în exteriorul unei funcții, aceasta este implicit globală. Nu este
necesar să se folosească cuvântul cheie global.
• Se utilizează cuvântul cheie global pentru a citi și scrie o variabilă globală, în interiorul
unei funcții.
• Utilizarea cuvântului cheie global în exteriorul unei funcții nu are efect.
Exemplul 3.21 #Accesarea variabilei globale din interiorul unei funcții
c = 3 # variabila globala implicita
def aduna():
print(c+5)
aduna()
Output
8
În exemplul de mai sus s-a citit valoarea variabilei globale c, dar fără ca aceasta să se modifice.
Pot fi însă situații când se dorește modificarea variabilei globale din interiorul funcției.
Exemplul 3.22 #Modificarea variabilei globale din interiorul funcției
c = 3 # variabila globala
def aduna():
c = 2*c #se modifica c, variabila globală în domeniul local
print(c + 5)
aduna()
Output
UnboundLocalError: local variable 'c' referenced before assignment
La rularea programului de mai sus, s-a obținut eroare deoarece din interiorul funcției se poate
doar citi variabila, dar nu se poate modifica. De fapt, în acest context, în interiorul funcției
variabila c este interpretată ca fiind o variabilă locală, neinițializată.
Soluția la problema de mai sus este utilizarea cuvântului cheie global.
Exemplul 3.23 #modificarea unei variabile declarate global în funcție
#Modificarea unei variabile globale din interiorul funcției utilizând
#cuvântul cheie global.
c = 3 # variabila globala
def aduna():
global c
c = 2*c # modifica c
print("In interiorul funcției aduna(), c = ", c)
aduna()
print("In exterior, c =", c)
Output
In interiorul funcției aduna(), c = 6
In exterior, c = 6
În secvența de program de mai sus, se atribuie inițial variabilei c valoarea 3. Apoi, în interiorul
funcției aduna(), se definește c utilizând cuvântul cheie global. În continuare, variabila c
se dublează, adică va fi c = 2*c. După aceasta, la apelul funcției aduna() se va imprima
valoarea variabilei globale c, dublate (c = 6). În acest caz, variabila c nu mai este interpretată
90 Funcții, module și pachete
ca fiind o variabilă locală neinițializată, ci ca o variabilă globală, căreia i-a fost alocată memorie
și a fost inițializată deja în exteriorul funcției aduna().
Observație. În Python, se recomandă crearea unui singur modul config.py pentru a conține
variabilele globale și a le distribui între module, în cadrul unei aceleiași aplicații formate din
mai multe module.
În exemplul care urmează se partajează și se testează două variabile globale între module.
Exemplul 3.20 #utilizarea unui fișier config.py conținând variabilele globale
# Se creează un fișier config.py, pentru a memora variabilele globale.
a = 10
b = "vid"
Output
Înainte de apelul fincuib, x = 20
Apelare fincuib
După apelarea fincuib, x = 20
x in program: 50
În programul de mai sus, s-a declarat o variabilă globală în interiorul unei funcții încuibate,
fincuib(). În interiorul funcției fprinc(), x nu este afectat de cuvântul cheie global. În
consecință, înainte și după apelul funcției fincuib(), variabila x ia valoarea unei variabile de
tip local, în acest caz, x = 20. În exteriorul funcției fprinc(), variabila x va lua valoarea
definită în funcția fincuib(), adică x = 50. Acest lucru e posibil pentru că s-a utilizat
cuvântul cheie global pentru x pentru a crea variabila globală în funcția fincuib().
Dacă se vor face schimbări în interiorul funcției fincuib(), acestea vor apărea în exteriorul
domeniului de vizibilitate locală, adică în program, dar nu și în fprinc().
Output
1.5
2.0
2.5
Observație. Numele args nu este obligatoriu. Se poate folosi orice nume de variabilă se
dorește.
Despachetarea argumentelor utilizând *
Dacă lista variabilă de argumente opționale este memorată sub forma unei liste sau a unui
tuplu, ca de exemplu: valori = [5, 6, 7] (listă), sau valori = (5, 6, 7) (tuplu), este necesar, pentru
ca valorile să fie preluate individual de funcție, să se utilizeze operatorul '*' pentru a
despacheta (unpack) valorile, astfel ca ele să poată fi trecute funcției. Exemplu:
media (3,4, *valori)
92 Funcții, module și pachete
B. Cazul **kwargs
În declarația sau definiția unei funcții poate să apară ca argument și **kwargs. Denumirea
kwargs este o prescurtare pentru keyworded - argumente care au nume și sunt elemente ale
unui dicționar – perechi cheie/valoare. În exemplul de mai jos, 7 și 9 sunt argumente de tip
non-keyworded, iar x și y sunt argumente de tip keyworded (chei într-un dicționar).
func_calc(7, 9, x = 4)
func_calc(7, 9, x = 4, y = 5)
Argumentele semnalizate prin **kwargs pot fi extrase utilizând o construcție for – in.
Exemplul 3.23 #se extrage o pereche cheie/valoare la fiecare iterație
def func_calc(nr1, nr2, **kwargs):
print(nr1)
print(nr2)
for i,j in kwargs.items():
print(i,j)
func_calc(7, 9, x = 7, y = 9)
Output
7
9
x 7
y 9
Într-o altă versiune a programului se pot extrage mai întâi cheile și apoi valorile
corespunzătoare, astfel:
Exemplul 3.24 #extrage cheile, apoi valorile pe baza cheilor
def func_calc(nr1, nr2, **kwargs):
print(nr1)
print(nr2)
for k in kwargs.keys():
print(k, kwargs[k])
func_calc(7, 9, x = 7, y = 9)
Output
7
9
x 7
y 9
3.2 Modulele
Definiție. Modulele sunt fișiere conținând instrucțiuni și declarații Python.
Un fișier conținând cod Python, de exemplu: calcul.py, este denumit modul, numele
modulului fiind calcul.
Modulele se utilizează pentru divizarea programelor de mare dimensiune în părți mai mici, mai
ușor de editat și organizat. În plus, modulele permit reutilizarea codului.
Cele mai utilizate funcții se pot defini ca module, astfel că ele se pot importa ori de câte ori
este nevoie, fără a fi rescrise din nou, în fiecare loc unde se utilizează.
93 Funcții, module și pachete
Modulele pot conține însă mai mult decât o funcție; ele pot conține mai multe funcții, clase sau
variabile.
Următorul exemplu reprezintă un modul care conține o singură funcție, aduna(), Această
funcție adună două numere, întoarce suma lor și poate fi apelată ori de cite ori este necesar.
Exemplul 3.25 # Un exemplu de modul. Numele său poate fi calcul.py
def aduna(a, b):
"""Programul aduna doua
numere si intoarce rezultatul"""
suma = a + b
return suma
Python este un limbaj care dispune de un număr foarte mare de module standard (în locația Lib
a instalării). Atât modulele standard, cât și modulele definite de utilizator, se importă la fel.
Pentru a afla numele tuturor modulelor (pre)instalate, se utilizează comanda:
>>> help("modules")
Pentru a afla doar numele unui/unor module care conțin o secvență de caractere, de exemplu
"run", se folosește comanda sub forma:
>>> help ("modules run")
Sunt disponibile două moduri de acces la definițiile funcțiilor conținute într-un modul:
păstrându-le denumirea sau redenumindu-le, astfel:
A. Se poate importa un modul folosind instrucțiunea import și accesând definițiile conținute
utilizând operatorul punct descris mai sus.
Exemplul 3.26 #instrucțiunea import, simplă
import math
print("Valoarea lui pi este =", math.pi)
Output
Valoarea lui pi este = 3.141592653589793
Output
Valoarea lui pi este = 3.141592653589793
S-a redenumit modulul math cu noul nume m. Motivul redenumirii poate fi scurtarea lungimii
numelui, sau o sugestivitate mai bună.
Observație. Vechiul nume math nu mai este recunoscut în domeniul de vizibilitate al
programului current, astfel că math.pi devine o denumire invalidă, varianta corectă fiind
m.pi.
Observație importantă. Importarea unui modul va avea ca efect executarea codului din
modulul importat înainte de executarea codului modulului care importă. Exemplul următor
clarifică această afirmație.
Exemplul 3.28 #Execuția modulului importat la momentul importării
Fie două module p1.py și p2.py. Modulul p2.py importă modulul p1.py.
Modulul p1.py
def f1():
print('Faza 1')
print('Faza 2')
Modulul p2.py
import p1
print('Faza 3')
p1.f1()
În exemplul de mai sus s-a importat doar constanta pi din modulul math, ignorându-se restul
modulului.
Observație. În acest caz de import fără redenumire, nu mai este necesară calificarea numelui
(utilizarea operatorului punct/dot ( . )).
Observație. Se pot importa mai multe definiții dintr-un modul fără a folosi operatorul punct,
cu o singură instrucțiune
95 Funcții, module și pachete
Se pot importa toate numele (definițiile) dintr-un modul folosind următoarea construcție:
from math import *
print("Valoarea lui pi este =", pi)
S-au importat toate definițiile din modul prin utilizarea simbolului *, cu excepția acelora care
încep cu underscore. Această metodă nu este totuși recomandată deoarece poate conduce la
dublarea unor definiții sau identificatori și dificultăți în gestionarea codului program.
Pentru a încărca un modul, se caută în mai multe locuri. Mai întâi se caută modulele
preconstruite (built-in) care ar putea avea același nume. Dacă nu se găsesc în biblioteca din
directorul implicit, Python caută în lista de directoare definite în sys.path, în următoarea
ordine:
• directorul curent.
• PYTHONPATH (variabila de mediu conținând o listă de directoare).
• directorul definit implicit la instalare.
>>> import sys
>>> sys.path
['', 'C:\\Program Files\\Python310\\Lib\\idlelib', 'C:\\Program
Files\\Python310\\python310.zip', 'C:\\Program Files\\Python310\\DLLs',
'C:\\Program Files\\Python310\\lib', 'C:\\Program Files\\Python310',
'C:\\Program Files\\Python310\\lib\\site-packages']
Calea conținând lista de directoare poate fi modificată de utilizator pentru adăugarea unei căi
specifice.
Interpretorul Python importă un modul doar o singură dată în timpul unei sesiuni.
Se presupune că se dorește încărcarea unui modul numit xmodul, al cărui conținut este:
# Demonstratia ca un modul
# se importa o singura data, chiar daca s-au facut mai
# multe importuri-reîncărcări
print("Această secvență program s-a executat")
Importarea acestui modul în interpretor generează rezultatul următor, care arată că importul s-
a efectuat o singură dată.
Exemplul 3.31 #se arată că importul unui modul se face doar o dată
96 Funcții, module și pachete
Pot să apară totuși situații care să facă necesară reîncărcarea unui modul, de exemplu modulul
se poate să se fi modificat. O cale eficientă (mai bună decât restartarea interpretorului) este
utilizarea funcției reload() care aparține modulului imp. Se procedează astfel:
Exemplul 3.32 #reîncărcarea unui modul cu funcția reload
>>>import imp
>>> import xmodul
Această secvență program s-a executat
>>> import xmodul
>>>imp.reload(xmodul)
Această secvență program s-a executat
Pentru aflarea numelor conținute într-un modul se poate utiliza funcția dir().
Mai sus s-a definit modulul calcul.py conținând funcția aduna(). Acum se poate aplica
funcția dir() modulului calcul:
>>> dir(calcul)
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'aduna']
Observație. Toate numele care încep cu underscore sunt atribute implicite Python asociate cu
modulul, nefiind definite de utilizator. De exemplu, atributul '__name__' conține numele
modulului importat (fără extensia .py).
>>>import calcul
>>>calcul.__name__
'calcul'
Observație importantă. Variabila '__name__' conține numele modulului, cu o singură
excepție: în modulul care este curent executat această variabilă are valoarea '__main__'.
Observație. Toate numele definite în spațiul de nume curent pot fi afișate utilizând funcția
dir() fără argumente.
Exemplul 3.33
>>> x=5
>>> y=34567
>>> alfa ='333'
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'alfa', 'x', 'y']
97 Funcții, module și pachete
3.3 Pachetele
3.3.1 Definiția pachetului
Definiție. O structură ierarhică compusă din module Python este denumită pachet. În Python
se poate face o analogie între aplicație și o structură ierarhică de pachete, între
director/subdirector și pachet/subpachet și între fișier și modul.
O aplicație poate fi compusă din mai multe fișiere. Acestea nu întotdeauna pot fi ținute în
aceeași locație. Mai potrivită este o structură ierarhică de directoare pentru ușurarea accesului
la fișiere. În cazul aplicațiilor formate din multe module, este avantajoasă plasarea modulelor
similare în același pachet, iar modulele diferite în pachete diferite. Această organizare face mai
simplă și mai clară gestionarea componentelor aplicației.
Achizitie_date
Temperatura.py
Se consideră o aplicație de tip “achiziție de date” care poate avea o structură de organizare a
pachetelor și modulelor ca in figura 3.1.
Se poate realiza importul modulelor din pachete utilizând operatorul punct (.).
98 Funcții, module și pachete
Dacă numele pare prea lung, se poate importa modulul fără a mai fi necesar să se utilizeze
prefixul, astfel:
from Achizitie_date.calcul import debit
O altă cale de import doar al funcției cerute (sau clasă, sau variabilă) dintr-un modul aflat într-
un pachet, se poate face astfel:
from Achizitie_date.calcul.debit import select_masa_volum
Deși pare mai simplă, această metodă nu este recomandată. Utilizarea complete a spațiului de
nume (namespace) asigură evitarea confuziilor și previne coliziunile datorate numelor dublate.
Observație. Și la importul modulelor din pachete se poate utiliza aliasul "as", redenumindu-
le.
Observație. Este importantă poziția relativă a modulului care importă față de modulul importat
în calea de acces sys.path. Modulul care importă trebuie să se afle în fața modulului importat
(să preceadă), adică mai spre baza structurii.
>>> print(suma(4,5))
9
3.4.3 Arătați prin exemplificare diferența dintre o funcție lambda și o funcție lambda anonimă.
R3.4.3 Funcția lambda:
>>> func = lambda x: x *x
>>> print (func(5))
25
Funcția lambda anonimă:
>>> (lambda x: x*x) (5)
25
3.4.4 Se poate spune că transmiterea unor parametri unei funcții în Python se poate face prin
valoare, ca în cazul limbajului C/C++ ?
a) Da b) Nu
R3.4.4 Nu. Se reamintește că în Python, nu se pot atribui în mod real valori variabilelor. Ceea
ce se întâmplă de fapt este memorarea în variabilă a referinței la obiect (adresa de memorie a
obiectului). În consecință, în Python nu poate exista transferul prin valoare ca în cazul
limbajului C/C++. Transferul se face prin referință, iar acest lucru poate cauza probleme în
cazul variabilelor mutabile (de exemplu o listă), adică o modificare în interiorul
funcției/obiectului, se transmite în exteriorul său (side effect). Dacă variabila este imutabilă (de
exemplu un tuplu), variabila nu se poate modifica. Cele spuse se pot verifica prin următorul
exemplu:
Fie funcţia:
def funct(xlista):
xlista[3] = 'albastru'
Dacă argumentul de apelare a funcției de mai sus este variabila ylista, definită ca mai jos,
ylista = ['alb', 'verde', 'rosu', 'negru']
se poate observa cum ylista a fost modificată după apel (are rol de variabilă globală):
def funct(xlista):
xlista[3] = 'albastru'
ylista = ['alb', 'verde', 'rosu', 'negru']
print('Lista initială', ylista)
funct(ylista)
print('Lista după apel', ylista)
Output
Lista initială ['alb', 'verde', 'rosu', 'negru']
Lista după apel ['alb', 'verde', 'rosu', 'albastru']
Efectul colateral este clar vizibil. Orice variabilă definită în afara funcției este variabilă globală
pentru acea funcție. O soluție pentru împiedicarea modificării, dacă se dorește acest lucru,
poate fi utilizarea unei variabile locale, care poate avea același nume cu variabila mutabilă
externă.
Concluzie: În Python nu există “transfer prin valoare” (funcția nu își face o copie proprie de
lucru a argumentului).
3.4.5 Se poate atribui o funcție unei variabile ?
a) Da b) Nu
R3.4.5
100 Funcții, module și pachete
Da. În exemplul de mai jos funcția funct() se atribuie variabilei y, care se va apela y().
def funct():
print("******")
y = funct
y()
Output
******
Output
10
21
b(a, 'textul-doi')
Output
textul-unu si textul-doi
Output
5
3
3.4.10 Ce se înțelege prin “semnătura unei funcții” ?
Numele funcției și lista sa de argumente sunt denumite “semnătura funcției”.
3.4.11 Cum se definește o funcție cu argumente opționale ?
R3.4.11 Argumentele pot fi definite prin atribuirea (utilizând =) unei valori implicite la numele
argumentului:
def fct(acceleratie = "zero") :
# .....
return actiune
Apelul acestei funcții este posibilă în trei feluri:
func("creste")
func(acceleratie = "scade")
func()
Capitolul 4
Output
<class 'int'>
<class 'float'>
(8+3j)
True
În timp ce întregii pot fi de orice lungime, un număr în virgulă flotantă are o precizie doar de
15 cifre zecimale.
În domeniul calculatoarelor se utilizează frecvent și alte sisteme de numerație în afară de cel
zecimal (baza 10): sistemele binar (baza 2), hexazecimal (baza 16) și octal (baza 8). În Python,
se pot reprezenta toate sistemele prin plasarea unor prefixe inaintea numerelor, astfel:
Sistemul numeric Prefixul
Binary '0b' sau '0B'
Octal '0o' sau '0O'
Hexazecimal '0x' sau '0X'
Exemplul 4.2 #reprezentarea în alte sisteme de numerație: binar, hexa și octal
print(0b1101011)
print(0xFB + 0b10)
print(0o15)
Output
107
253
103 Tipuri numerice de date în Python
13
Rezultatul de mai sus apare ca urmare a reprezentării numerelor în sistemul de numerație binar
utilizat de calculator. Numerele în virgulă flotantă sunt exprimate ca fracțiuni binare deoarece
calculatorul înțelege doar 0 și 1. Din acest motiv, cele mai multe din fracțiunile zecimale nu
pot fi stocate precis în memorie.
Exemplul 4.5 #erori la reprezentarea numerelor datorate calculatorului
- Numărul fracționar 1/3 nu poate fi reprezentat precis ca număr zecimal, deoarece rezultatul
împărțirii lui 1 cu 3 produce 0.33333333..., care are o lungime infinită și nu poate fi aproximat.
- Numărul zecimal fracționar 0.1 este reprezentat în binar printr-un șir infinit lung
0.000110011001100110011... , iar calculatorul poate stoca doar un număr finit de biți, doar
aproximând pe 0.1. Ca urmare eroarea apărută aparține calculatorului și nu limbajului Python.
Exemplul 4.6 #eroarea dată de calculul cu precizie finită
>>> 1.1 + 2.2
104 Tipuri numerice de date în Python
3.3000000000000003
Output
0.1
0.1000000000000000055511151231257827021181583404541015625
Acest modul efectuează calculele cu mai mare precizie decât virgula flotantă. Ca urmare se vor
obține rezultate mult mai acceptabile, sau mai semnificative la operațiunile cu mărimi fizice,
fiind permisă alegerea numărului de zecimale afișabile.
Exemplul 4.8 #utilizarea modulului decimal
from decimal import Decimal as D
print(D('1.1') + D('2.2')) #adunare
print(D('1.5') * D('2.50')) #înmulțire
Output
3.3
3.750
Observație. Deși calculul zecimal este mai precis, se preferă calculele folosind virgula flotantă
din motive de execuție mai rapidă.
Observație. Calculul zecimal utilizând modulul decimal este de preferat în următoarele
situații:
- Calcule financiare.
- Se dorește controlul nivelului de precizie a calculelor.
- Se face limitare la un număr semnificativ de poziții zecimale.
Output
105 Tipuri numerice de date în Python
3/2
5
1/3
La utilizarea funcției Fraction din modulul fractions, pot apărea uneori rezultate imprecise,
datorate reprezentării imperfecte a modelului virgulei flotante în binar.
În compensație, Fraction permite afișarea cu șiruri. Această opțiune este preferată la lucrul
cu numere zecimale.
Exemplul 4.10 #afișarea rezultatului ca șir de caractere
import fractions
print(fractions.Fraction(1.1)) #rezultatul va fi un număr float
print(fractions.Fraction('1.1')) #rezultatul va fi o fracție
Output
2476979795053773 / 2251799813685248 #raportul a două numere de 16 cifre
11/10 #raportul a două numere reprezentate
#ca șir
Cu tipul fracționar, utilizând modulul fractions, sunt permise toate operațiunile aritmetice
de bază.
Exemplul 4.11 #operații aritmetice cu funcția Fraction()
from fractions import Fraction as F
print(F(1, 3) + F(1, 3))
print(1 / F(5, 6))
print(F(-3, 10) > 0)
print(F(-3, 10) < 0)
Output
2/3
6/5
False
True
Output
3.141592653589793
-1.0
106 Tipuri numerice de date în Python
22026.465794806718
3.0
3.1760912590556813
1.1752011936438014
10.017874927409903
720
Output
19
e
['d', 'e', 'a', 'b', 'c']
0.8707283154064434
R4.6.3 răspuns: b
4.6.4 Care număr este incorect:
a. x = 0b101 b. x = 0x4c8 c. x = 03783
R4.6.4 c
4.6.5 Care este ieșirea instrucțiunii următoare:
>>>9 // 2
a) 4.5 b) 4.0 c) 4
R4.6.5 răspuns: c
4.6.6 Care este valoarea lui x,valoara sa fiind dată de expresia x = math.factorial(0)?
a. 0 b. 1 c. error
R4.6.6 Răspuns: b
4.6.7 Ce se obține la execuția instrucțiunii: print(math.fabs(5.4)) ?
a. -5.4 b. 5.4 c. 5
R4.6.7 b
4.6.7 Care este utilitatea modulului operator în Python ? Exemplificați.
R4.6.8 Modulul operator conține și exportă în Python un set de funcții eficiente, scrise în
limbajul C, corespunzând operatorilor intrinseci din Python. Denumirile sunt atribuite atât
funcțiilor speciale (cu prefix și sufix dublu underscore), cât și funcțiilor obișnuite. De exemplu:
>>>import operator
>>>operator.add(2,3) #echivalent cu +
5
>>>operator.mul(32,56) #echivalent cu *
1792
>>>operator.truediv(25,6) #echivalent cu /
4.166666666666667
>>>operator.mod(25,6) #echivalent cu %
1
108 Tipuri numerice de date în Python
4.6.9 Cum se indică încercarea de reprezentare a unui număr float peste limitele posibile în
Python?
R4.6.9 La încercarea de obținere a unui număr float peste capacitatea de reprezentare,
răspunsul Python va fi inf, sau -inf, după caz.
>>>nr = 2e500
>>>nr
inf
>>>type(nr)
<class 'float'>
>>>print(-2e500)
-inf
Mai mult, impresia că un număr ca 0.1 este un număr exact este greșită. Dacă se execută o
comandă print(x), se afișează implicit doar primele 16 zecimale, ceea ce înseamnă că două
numere fracționare care diferă printr-o valoare sub un anumit prag, pot avea aparent aceeași
valoare. Dacă se impune precizia, se poate observa acest lucru.
x = 0.1
>>>print(x)
0.1
>>>print('%1.35f ' %x)
0.10000000000000000555111512312578270
4.6.11 Ce probleme pot apărea la rotunjirea numerelor datorate erorilor de reprezentare? Care
este strategia Python privind rotunjirea?
R4.6.11 La utilizarea unui program ca round() pentru rotunjirea numerelor, din cauza erorilor
de reprezentare, se pot obține rezultate imprevizibile, deoarece așteptările sunt ca rotunjirea să
se facă fie numai la numărul întreg superior (cel mai frecvent), fie numai la cel inferior:
>>>round(2.5)
2
>>>round(3.5)
4
Strategia Pythonului este ca rotunjirile să se facă la numărul par (rounding ties to even),
conform standardului IEEE 754. Prin legătură (tie) se înțelege numărul având cifra 5 aflată pe
ultimul loc al părții fracționare. De exemplu, 5.1415 este o legătură (tie), dar nu și 4.7. Acest
lucru explică rezultatul de mai sus.
Rotunjirea cu round() funcționează și precizând un număr de zecimale la care aceasta se poate
face.
>>>round(3.2685)
3
>>>round(3.2685, 1)
3.3
>>>round(3.2685, 2)
3.27
109 Tipuri numerice de date în Python
>>>round(3.2685, 3)
3.268
Funcția math.ceil() rotunjește un număr zecimal la valoarea întregului superior, cel mai
apropiat:
>>>math.ceil(4.35)
5
math.ceil(-4.35)
-4
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 5
Liste
5.1 Precizare
Acest capitol este dedicat în principal tipului listă din Python. Deorece tipul tablou este
absent în limbajul Python, va fi tratat și aspectul utilizării modulului array pentru suplinirea
absenței tipului tablou (doar unidimensional!) prin liste,.
De asemenea, se va detalia lucrul cu listele de liste, care pot suplini absența tipului tablou
bidimensional.
Se poate utiliza operatorul index [] pentru accesarea elementelor unei liste. În Python,
indicii încep de la zero. De exemplu, o listă cu 12 elemente va avea un index de la 0 la 11.
Încercarea folosirii unui index dincolo de limite va produce eroarea IndexError.
Indexul trebuie să fie un intreg. Alegerea unui alt tip de index decât întreg va produce
TypeError.
Output
n
g
u
i
6
Traceback (most recent call last):
. . . . . . . . . .
print(lista_incuibata[4.0]) #eroare de tip de index
TypeError: list indices must be integers or slices, not float
În Python este permisă indexarea negativă pentru tipurile de date de tip secvență. Indexul
egal cu -1 se referă la ultimul element, indexul -2 se referă la penultimul element, etc.
Exemplul 5.3 #indexarea negativă
lista = ['n','e','g','r','u']
print(lista[-1])
print(lista[-5])
Output
u
n
Output
l
u
['i', 'v', 'e']
['u', 'n', 'i', 'v']
['r', 's', 'a', 'l']
['u', 'n', 'i', 'v', 'e', 'r', 's', 'a', 'l']
u
Slicing-ul poate fi văzut cel mai bine considerând indexul între două elemente. Dacă se
dorește a se accesa un domeniu, este nevoie de doi indici pentru a extrage o porțiune din listă.
Lista u n i v e r s a l
Indexul pozitiv 0 1 2 3 4 5 6 7 8
Indexul negativ -9 -8 -7 -6 -5 -4 -3 -2 -1
112 Liste
Output
[1, 4, 6, 8]
[1, 3, 5, 7]
Se poate adăuga un element la o listă utilizând metoda append(), sau mai multe elemente
cu metoda extend().
Exemplul 5.6 #ilustrare metoda extend()
lista = [15, 31, 52]
lista.append(7)
print(lista)
lista.extend([9, 22, 13])
print(lista)
Output
[15, 31, 52, 7]
[15, 31, 52, 7, 9, 22, 13]
Definiție. Se poate utiliza operatorul + pentru combinarea a două liste. Acest proces se
numește concatenare.
Observație. Operatorul * repetă o listă de un număr dat de ori.
Exemplul 5.7 #operatorii + și *
lista = [1, 3, 5]
print(lista + [9, 7, 5]) # Concatenarea listelor
print(["text"] * 3) # repetarea listelor
Output
[1, 3, 5, 9, 7, 5]
[' text', 'text', 'text']
În plus, se poate insera un element la o locație dorită prin utilizarea metodei insert(), sau
se pot insera multiple elemente în interiorul unei liste utilizând metoda slice.
Metoda insert() are doi parametri: primul indică poziția de inserare, iar al doilea
precizează elementul inserat.
Exemplul 5.8 #inserare cu metodele insert() și slice
lista = [1, 9]
lista.insert(1,3) # Utilizare metoda insert()
print(lista)
113 Liste
Output
[1, 3, 9]
[1, 3, 5, 7, 9]
Output
['p', 'r', 'b', 'l', 'e', 'm', 'a']
['p', 'm', 'a']
Traceback (most recent call last):
. . . . . .
print(lista) # Eroare: Lista nedefinită
NameError: name 'lista' is not defined
Se poate utiliza metoda remove() pentru eliminarea unui element dat sau metoda pop()
pentru a elimina un element al cărui index este dat.
În caz că indexul nu este dat, se elimină ultimul element al listei. Astfel, se poate implementa
modelui de stivă de date.
Pentru ștergerea tuturor elementelor unei liste se utilizează funcția clear().
Exemplul 5.10 #ștergere elemente cu remove(), pop(), clear()
lista = ['p', 'r', 'o', 'b', 'l', 'e', 'm', 'a']
lista.remove('p')
print(lista)
print(lista.pop(1)) #sterge (extrage) 'o'
print(lista)
print(lista.pop()) #sterge (extrage)'a'
print(lista)
lista.clear()
print(lista)
Output
['r', 'o', 'b', 'l', 'e', 'm', 'a']
o
['r', 'b', 'l', 'e', 'm', 'a']
a
['r', 'b', 'l', 'e', 'm']
[]
114 Liste
De asemenea, se pot șterge elemente din lista prin atribuirea unei liste vide unei felii (slice)
de elemente.
Exemplul 5.11 #ștergere elemente din listă prin metoda slice
lista = ['p', 'r', 'o', 'b', 'l', 'e', 'm', 'a']
lista[2:3] = [] #se sterge 'o' = elementul 2
print(lista)
lista[2:5] = [] #se sterg 'b', 'l', 'e' = elementele 2,3,4
print(lista)
Output
['p', 'r', 'b', 'l', 'e', 'm', 'a']
['p', 'r', 'm', 'a']
Observație. Enumerarea metodelor obținute cu comanda dir() este mai sumară comparativ
cu aceea obținută cu comanda help(). În acest ultim caz se obțin mai multe informații, de
exemplu o scurtă descriere și lista argumentelor cu care se apelează metoda. În unele cazuri,
dacă textul care trebuie afișat este foarte mare, se afișează textul Squeezed text(nr.
linii). Pentru a expanda textul se aplică un dublu click pe acest mesaj.
Output
1
2
[0, 1, 3, 4, 6, 8, 8, 12]
[12, 8, 8, 6, 4, 3, 1, 0]
115 Liste
Output
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
Output
[2, 4, 6, 8]
Output
['K', 'J', 'H', 'G', 'F']
O “listă comprehensivă” poate conține opțional mai multe instrucțiuni for sau instrucțiuni
if. O instrucțiune opțională if poate fi un filtru la generarea unei noi liste, ca în următoarele
exemple.
Exemplul 5.16 #lista comprehensivă utilizând for și if
>>> pow2 = [2 ** x for x in range(10) if x > 5]
>>> pow2
[64, 128, 256, 512]
Se poate testa dacă un element există într-o listă sau nu, utilizând cuvântul cheie in.
Exemplul 5.19 #test apartenență la listă
lista = ['c', 'a', 'l', 'c', 'u', 'l', 'a', 't', 'o', 'r']
print('t' in lista)
print('s' in lista)
print('w' not in lista)
Output
True
False
True
O listă poate fi parcursă element cu element (iterată) cu ajutorul unei bucle for.
Exemplul 5.20 #parcurgerea unei liste cu for și in
for culori in ['rosu','galben','albastru']:
print("O culoare a steagului este:", culori)
Output
O culoare a steagului este: rosu
O culoare a steagului este: galben
O culoare a steagului este: albastru
elemente - elementele tabloului. Sunt accesate prin operațiunea de indexare. Indexul este un
întreg și începe cu valoarea zero.
Tablourile unidimensionale (1D), denumite și vectori, au ca elemente simple valori.
Observație importantă. Modulul array nu suportă structuri bidimensionale (2D). Pentru
lucrul cu acestea se recomandă utilizarea listelor de liste (care simulează tablourile 2D), sau
utilizarea modulului numpy.
tip_data este codul tipului (“typecode”), o literă utilizată pentru a defini tipul valorii
memorate în tablou. Codurile sunt următoarele:
b = numere întregi cu semn, reprezentate pe 1 octet.
B = numere întregi fără semn, reprezentate pe 1 octet.
u = caracter Unicode reprezentat pe 1 octet.
h, i = numere întregi cu semn reprezentate pe 2 octeți.
H, I = numere întregi fără semn reprezentate pe 2 octeți.
l = numere întregi cu semn reprezentate pe 4 octeți.
L = numere întregi fără semn reprezentate pe 4 octeți.
q = numere întregi cu semn reprezentate pe 8 octeți.
Q = numere întregi fără semn reprezentate pe 8 octeți.
f = numere reprezentate în virgulă flotantă pe 4 octeți.
d = numere reprezentate în virgulă flotantă pe 8 octeți.
Obiectele de tip array au atributele typecode și itemsize și o lungă listă de metode, dintre
care foarte utile sunt: append(), clear(), copy(), count(), extend(), index(),
inser(), pop(), remove(), reverse(), sort(), fromlist(), etc.
Output
s t e l u t e : ✱ ✻ ⊛ 1 2 3 4 5 1.0 2.0 3.14
Output
2.56
320.88
760.123
23.56
44.965
Output
2.56
23.56
B1. Inserția – adăugarea unui element la un index dat.
Exemplul 5.24 #Adaugarea unui element folosind metoda insert()
from array import *
xarray = array('d', [2.56,320.88,760.123,23.56,44.965])
xarray.insert(1,50.2)
for x in xarray:
print(x)
Output
2.56
50.2
320.88
760.123
23.56
44.965
Output
2.56
320.88
23.56
119 Liste
44.965
D1. Căutarea – găsirea unui element utilizând fie indexul, fie valoarea elementului.
Exemplul 5.26 #găsirea unui element prin index sau a poziției sale prin valoarea sa
from array import *
xarray = array('d', [2.56,320.88,760.123,23.56,44.965])
print(xarray[2]) #cautarea valorii utilizind indexul
print(xarray.index(23.56)) #cautarea indexului utilizand valoarea
Output
760.123
3
E1. Actualizarea – modificarea unui element având un index dat.
Exemplul 5.27 #Actualizarea prin reatribuirea unei noi valori elementului avand un index dat
from array import *
xarray = array('d', [2.56,320.88,760.123,23.56,44.965])
xarray[2] = 100
for x in xarray:
print(x)
Output
2.56
320.88
100.0
23.56
44.965
Output
[[8, 9, 7, 10], [10, 9, 10, 9], [9, 10, 8, None], [7, 8, 6, 5]]
[8, 9, 7, 10]
[9, 10, 8, None]
10
[10, 9, 10, 9]
None
Intreg tabloul:
8 9 7 10
10 9 10 9
9 10 8 None
7 8 6 5
Output
8 9 9 10
10 9 10 9
9 5 10 7
9 10 8 None
7 8 8 9
Observație. Tabloul poate să nu conțină None pentru elementele lipsă. Totuși, se va
semnaliza “index inexistent”, dacă se va dori acces la acele poziții.
for c in r:
print(c,end = " ")
print()
Output
8 9 9 10
0 9 10 9
9 10 8 None
D2. Căutarea – găsirea unui element utilizând fie indexul, fie valoarea elementului.
TB = [[8, 9, 9, 10], [10, 9, 10, 9], [9, 10, 8, 9], [7, 8, 8, 9]]
print(TB[3][2]) #cautarea valorii utilizind indexul
#se cauta toate elementele TB = 8
n = 8
for i in range(4): #cautarea indexului utilizand valoarea
for j in range(4):
# print("i=", i, "j=", j, TB[i][j])
if TB[i][j] == n:
print("TB[", i, ",", j, "]")
Output
8
TB[ 0 , 0 ]
TB[ 2 , 2 ]
TB[ 3 , 1 ]
TB[ 3 , 2 ]
Output
8 9 8 10
10 9 10 9
9 7 6 5
7 8 8 9
Aplicațiile în Python în care se solicită netezirea listelor sunt frecvente și din acest motiv s-au
dezvoltat mai multe tehnici de flattening.
a. Netezirea unei liste de liste utilizând bucla for
Exemplul 5.32 #netezirea unei liste de liste cu bucla for
lista_de_liste = [[7,4,3,6],["alfa", "beta", "gama"], [5.2, 2+3j,34.92,
11.23, 5, 645]]
lista_neteda = []
for lst in lista_de_liste:
for termen in lst:
lista_neteda.append(termen)
print(lista_neteda)
Output
[7, 4, 3, 6, 'alfa', 'beta', 'gama', 5.2, (2+3j), 34.92, 11.23, 5, 645]
Output
[7, 4, 3, 6, 'alfa', 'beta', 'gama', 5.2, (2+3j), 34.92, 11.23, 5, 645]
Output
Linux #Rularea 1
Microsoft #Rularea 2
Output
[3, 2, 1, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
123 Liste
Output
Gutuie
Pruna
Para
Cireasa
Output
2 1 3
3 2 1
#Inversarea numerelor
lista2 = [21,35,16,44,22]
print (lista2 [:: - 1])
['d', 'c', 'b', 'a']
[22, 44, 16, 35, 21]
Output
['d', 'c', 'b', 'a']
[22, 44, 16, 35, 21]
Output
Tabloul de numere intregi este: 1 2 3
Tabloul de numere reale este: 2.5 3.2 3.3
import array as ar
x_arr = ar.array('i', [1,2,3,4,5])
print(x_arr)
x_ext_arr = ar.array('i', [6,7,8,9,10])
print(x_ext_arr)
x = x_arr.extend(x_ext_arr)
print(x_arr)
Output
array('i', [1, 2, 3, 4, 5])
array('i', [6, 7, 8, 9, 10])
array('i', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 6
Tupluri
6.1 Definiția și crearea tuplurilor
Definiție. Un tuplu este asemănător unei liste, cu deosebirea că elementele sale sunt fixe (nu
pot fi modificate așa cum este posibil într-o listă). Un tuplu poate avea oricâți membri, de
diferite tipuri (integer, float, listă, șir, etc.).
Crearea unui tuplu. Un tuplu este creat prin plasarea tuturor elementelor sale între paranteze
rotunde (), separate prin virgule. Totuși, parantezele sunt opționale, folosirea lor fiind doar
recomandată.
Exemplul 6.1 # crearea diferitelor tipuri de tupluri
# Tuplu vid
tuplu = ()
print(tuplu)
# Tuplu format din intregi
tuplu = (1, 2, 3)
print(tuplu)
# Tuplu conținand diferite tipuri de data
tuplu = (1, "Buna ziua", 3.4)
print(tuplu)
# Tuplu încuibat
tuplu = ("albastru", [8, 4, 6], (1, 2, 3))
print(tuplu)
Output
()
(1, 2, 3)
(1, 'Buna ziua', 3.4)
('albastru', [8, 4, 6], (1, 2, 3))
Un tuplu poate fi creat fără utilizarea parantezelor, prin asa numita operație de “împachetare a
tuplului”. Este posibilă, de asemenea, “despachetarea tuplului”.
Exemplul 6.2 #împachetarea și despachetarea tuplului
>>> tuplu = 6, 5.5, "verde"
>>> tuplu
(6, 5.5, 'verde')
>>> a, b, c = tuplu
>>> print(a)
6
>>> print(b)
5.5
>>> print(c)
verde
Observație. Crearea unui tuplu cu un singur element implică un artificiu, deoarece existența
unui element între paranteze nu este suficientă. Este necesar să se adauge o virgulă, pentru a
se preciza că este vorba, de fapt, de un tuplu.
Exemplul 6.3 #creare tuplu cu un element
# Fara virgula se creeaza un sir
126 Tupluri
tuplu = ("violet")
print(type(tuplu))
# Crearea unui tuplu cu un element
tuplu = ("violet",)
print(type(tuplu))
# Creare tuplu fără paranteze
tuplu = "violet",
print(type(tuplu))
Output
<class 'str'>
<class 'tuple'>
<class 'tuple'>
Indexarea utilizează operatorul index [] pentru accesarea unui element aparținînd unui tuplu.
Valorile indecșilor încep de la 0. Indexul unui tuplu care are 8 elemente are valori între 0 și 7.
Încercarea de accesare a unui index în afara domeniului indexului va produce o eroare de index
de tipul IndexError.
Tipul indexului trebuie să fie întreg. Nu se pot utiliza indecși de tipul float sau alte tipuri
deoarece apare ca rezultat al execuției mesajul TypeError.
Tuplurile încuibate pot fi accesate prin utilizarea indexării încuibate, ca în exemplul următor.
Exemplul 6.4 #accesare tuplu încuibat
# Accesarea tuplului prin indexare
tuplu = ('g','a','l','b','e','n') #interval de valori index: 0 la 5
print(tuplu[0]) # 'g'
print(tuplu[5]) # 'n'
# tuplu încuibat
tuplu = ("verde", [10, 12, 14], (11, 13, 15))
print(tuplu[0][3])
print(tuplu[1][1])
# print(tuplu[6]) # IndexError: index în afara domeniului
# Indexul trebuie să fie un întreg
tuplu[2.0] # eroare de tip: TypeError (indexul nu este întreg)
Output
g
n
d
12
Traceback (most recent call last):
. . . . . .
tuplu[2.0] # eroare de tip: TypeError (indexul nu este întreg)
TypeError: tuple indices must be integers or slices, not float
127 Tupluri
Definiție.Slicingul este o metodă prin care se poate accesa un domeniu de elemente al unui
tuplu prin utilizarea operatorului “colon” (:).
Exemplul 6.6 # Accesarea elementelor unui tuplu utilizând slicingul
tuplu = ('u','n','i','v','e','r','s','a','l')
# elementele al 2-lea până la al 4-lea
print(tuplu[1:4])
# elementele începând cu al 2-lea
print(tuplu[:-7])
# elementele al 8-lea până la sfârșit
print(tuplu[7:])
# elementele de la început până la sfârșit
print(tuplu[:])
Output
('n', 'i', 'v')
('u', 'n')
('a', 'l')
('u', 'n', 'i', 'v', 'e', 'r', 's', 'a', 'l')
Slicingul poate fi imaginată considerând că indexul se raportează la elementele tuplului ca în
exemplul de mai jos. Dacă se dorește accesarea unui domeniu, este necesar ca indexul să fie
specificat corespunzător domeniului din tuplu.
u n i v e r s a l
0 1 2 3 4 5 6 7 8
-9 -8 -7 -6 -5 -4 -3 -2 -1
Singurul caz în care funcționează reatribuirea elementelor, în cazul unui tuplu, este acela în
care atribuirea este totală, nu parțială.
Exemplul 6.7 # Elementele unui tuplu sunt imutabile
tuplu = (4, 2, 3, [6, 5])
# tuplu[1] = 9
Output
Traceback (most recent call last):
…
tuplu[1] = 9
TypeError: 'tuple' object does not support item assignment
Output
(4, 2, 3, [9, 5])
('m', 'o', 'n', 'd', 'i', 'a', 'l')
Se poate utiliza operatorul + pentru a combina două tupluri. Acest proces este denumit de obicei
concatenare.
Se pot repeta elementele într-un tuplu, de un număr precizat de ori, utilizând operatorul *.
Rezultatul aplicării operatorilor + și * este producerea de noi tupluri.
Exercițiul 6.9 #concatenarea
# Concatenare
print((1, 2, 3) + (4, 5, 6))
# Repetare
print(("Ploaie",) * 3)
Output
(1, 2, 3, 4, 5, 6)
(' Ploaie', ' Ploaie', ' Ploaie')
Output
129 Tupluri
Se poate testa dacă un element există într-un tuplu, folosind cuvântul cheie in.
Output
True
False
True
Se poate utiliza bucla de tip for pentru parcurgerea (iterarea) tuturor elementelor unui tuplu.
Output
Buna ziua, Ion
Buna ziua, Maria
• Deoarece tuplurile sunt imutabile, iterarea printr-un tuplu este mai rapidă decât printr-o listă
(tuplurile sunt ușor mai performante comparativ cu listele).
• Tuplurile, care conțin elemente imutabile, pot avea rolul de cheie în dicționare. Listele nu
permit acest lucru.
• Dacă aplicația utilizează date care trebuie să rămână fixe, protejarea lor la schimbare se
poate face prin declararea lor ca tupluri.
6.7.4 Să se scrie o secvență de program pentru a verifica existența unui element într-un tuplu.
R6.7.4
131 Tupluri
xtuplu = ("a", 1,2,3, "b", "o", "t", "c", "d", "$", 20)
print("h" in xtuplu)
False
print("$" in xtuplu)
True
6.7.5 Să se scrie o secvență de program care să găsească numărul de repetări al unui element
în tuplu.
R6.7.5
>>>xtuplu = ("a", 1,2,3, "b", "a", "t", "c", "a", "$", 20)
>>>repetari_a = xtuplu.count("a")
>>>print(repetari_a)
3
R6.7.9 c)
R6.7.10 c
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 7
Șiruri
7.1 Conceptul de șir
Definiție. Un șir este o secvență de caractere (caracterul este un simbol: cifră, literă a
alfabetului, semn de punctuație, etc.).
Calculatoarele utilizează caracterele indirect, prin intermediul codurilor corespunzătoare lor în
binar. În memoria calculatorului, orice caracter este reprezentat ca o combinație de 0 și 1.
Conversia unui caracter într-un număr binar se numește codare, iar operația inversă, decodare.
Cele mai răspândite și mai utilizate sisteme de coduri sunt ASCII și Unicode.
In Python, un șir este o secvență de caractere Unicode. Spre deosebire de ASCII, limitat la cele
26 de litere ale alfabetului englez și având în total maximum 256 de coduri, Unicode, dezvoltat
mai târziu, include coduri pentru toate limbile și are și alte particularități.
Output
Buna ziua!
Buna ziua!
Buna ziua!
Buna ziua,
bine ati venit!
Python permite indecși negativi și în cazul șirurilor. Indexul egal cu -1 va referi ultimul element
al șirului, -2 va referi penultimul (al doilea de la sfârșit spre început), etc. Se poate accesa un
domeniu în șir prin utilizarea operatorului colon (:).
Exemplul 7.2 #Accesarea unui șir de caractere
sirul = 'activitate'
print('sirul = ', sirul)
print('sirul[0] = ', sirul[0]) #primul caracter
print('sirul[-1] = ', sirul[-1]) #ultimul caracter
print('sirul[1:5] = ', sirul[1:5]) #domeniul incepand cu al doilea,
#sfarsind cu al cincilea caracter
print('sirul[5:-2] = ', sirul[5:-2]) #domeniul incepand cu al 6-lea,
#sfarsind cu al 8-lea caracter
Output
sirul = activitate
sirul[0] = a
sirul[-1] = e
sirul[1:5] = ctiv
sirul[5:-2] = ita
Dacă se încearcă accesarea unui index în afara intervalului definit, sau se utilizează ca indecși
altceva decât întregi, se va obține o eroare.
Exemplul 7.3 # indexul trebuie să fie în interiorul domeniului
>>> sirul[15]
Output
...
IndexError: string index out of range
Output
...
TypeError: string indices must be integers
Output
. . . . .
sirul[5] = '4'
TypeError: 'str' object does not support item assignment
Output
'Python'
În concluzie, nu se pot sterge sau elimina caractere dintr-un șir. Este posibilă totuși ștergerea
întregului șir cu cuvântul cheie del.
Output
...
TypeError: 'str' object doesn't support item deletion
Output
...
NameError: name 'sirul' is not defined
Output
sir1 + sir2 = Buna Ziua!
Output
Sir1 * 3 = BunaBunaBuna
De asemenea, simpla punere alăturată prin scriere a două șiruri produce concatenarea (la fel
ca operatorul +).
Exemplul 7.10 #concatenare prin lipirea șirurilor
>>> 'Buna ''Ziua!'
Output
'Buna Ziua!'
Dacă șirurile concatenate se întind pe linii diferite, atunci este necesară folosirea parantezelor.
Exemplul 7.11 # utilizarea parantezelor
>>> ('Buna '
... 'Ziua!')
Output
'Buna Ziua!'
Output
4 litere gasite
Output
enumerare_caractere_sir = [(0, 'o'), (1, 'l'), (2, 't'), (3, 'e'), (4,
'a'), (5, 'n')]
lungime_sir = 6
Exemplul 7.15 #eroare la imprimarea unui text cu ghilimele fără secvența escape
>>> print("Filmul se numeste "D'ale lui Pacala"")
Output
...
SyntaxError: invalid syntax
O cale de a ocoli aceste probleme este utilizarea ghilimelelor triple. O altă cale este utilizarea
secvențelor Escape.
O secvență Escape începe cu un backslash (\) , fiind interpretată diferit. Dacă se utilizează o
ghilimea simplă pentru reprezentarea unui șir, toate ghilimele simple din interiorul șirului
trebuie să fie marcate ca Escape. Asemănător în cazul ghilimelelor duble.
Exemplul 7.16 #utilizare secvențe Escape
# utilizarea ghilimelelor triple
print('''Filmul se numeste "D'ale lui Pacala"''')
Output
Filmul se numeste "D'ale lui Pacala"
Filmul se numeste "D'ale lui Pacala"
Filmul se numeste "D'ale lui Pacala"
Uneori se dorește imprimarea unui șir fără modificările determinate de secvențele Escape.
Acest lucru se poate face prin punerea unui r sau R în fața șirului. Ca urmare, secvențele Escape
din interiorul șirului vor fi ignorate.
Exemplul 7.18 #imprimarea cu ignorarea secvențelor Escape
>>> print("Imprimare sir \n pe un singur rand")
Imprimare sir
pe un singur rand
>>> (r"Imprimare sir \n pe un singur rand")
'Imprimare sir \\n pe un singur rand'
Output
--- Ordonare naturala ---
Mihai, George si Stefan
Metoda format() poate avea specificații formale, separate de numele câmpului prin colon (:).
Exemplu de specificații referitoare la șiruri: alinere la stânga (<), aliniere la dreapta (>), sau
centrare (^).
De asemenea, se pot formata întregii ca binar, hexazecimal, etc., iar numerele fracționare (float)
pot fi rotunjite sau afișate în format exponențial. Există un număr mare de metode de formatare
variată a șirurilor în vederea reprezentării la ieșire.
Exemplul 7.20 #formatarea numerelor
>>> # formatarea întregilor
>>> "Reprezentarea binara a lui {0} este {0:b}".format(12)
' Reprezentarea binara a lui 12 este 1100'
>>> # rotunjirea
>>> "O treime este: {0:.3f}".format(1/3)
'O treime este: 0.333'
Este posibil și stilul de formatare mai vechi (cunoscut din limbajul C standard), cu ajutorul
operatorului %.
Exemplul 7.21 #formatarea ca în limbajul C standard
>>> x = 12.3456789
>>> print('Valoarea lui x este: %3.2f' %x)
Valoarea lui x este: 12.35
>>> print(' Valoarea lui x este: %3.4f' %x)
Valoarea lui x este: 12.3457
>>>nume = 'Dorel'
>>>varsta = 25
>>print('Varsta lui %s este %d ani'%(nume, varsta))
Varsta lui Dorel este 25 ani
Formatarea literalelor șir prin metoda “f-strings” a apărut în versiunea Python 3.6 și a introdus
o simplificare semnificativă a printării. Șirurile “f-strings” au un f (sau F) la început și acolade
conținând expresii (chiar și funcții) care vor fi înlocuite de valori. Expresiile (și/sau funcțiile,
dacă este cazul) sunt evaluate la execuție și formatate cu protocolul __format__.
Observație. Prefixul “f” provine de la cuvântul “fast” (rapid).
Exemplul 7.22 #formatarea cu prefix f (metoda f-strings)
>>>nume = 'Mihai'
>>>nota = 10
>>>print(f'Lucrarea lui {nume} a primit nota {nota})
Lucrarea lui Mihai a primit nota 10
>>>print(F'Lucrarea lui {nume} a primit nota {nota})
Lucrarea lui Mihai a primit nota 10
#utilizarea și evaluarea metodei upper()
print(f'Lucrarea lui {nume.upper()} a primit nota {nota}')
Lucrarea lui MIHAI a primit nota 10
Dacă se dorește precizarea unui format la imprimarea variabilei se poate utiliza următoarea
formă:
>>>n = 6.837
>>>f'Valoarea lui n este {n:.2f}'
'Valoarea lui n este 6.84'
Colonul (:) după numele variabilei precizează că tot ce urmează aparține formatului specificat.
Se poate insera între acolade și virgula, pentru a preciza separatorul cifrelor părții întregi:
>>>n = 1234567890.12
>>>f"Valoarea lui n este {n:,.2f}"
'Valoarea lui n este 1,234,567,890.12'
De asemenea, se poate afișa procentajul fără, sau cu una sau mai multe zecimale:
>>>p = 0.155
>>>f'Taxa este de {p:.2%}'
'Taxa este de 15.50%'
7.8.6 Creați un șir format din 25 de liniuțe "-" utlizând operatorul de multiplicare.
R7.8.6
>>> x = '-'
>>> y = 25*x
>>> print(y)
---------------------
7.8.7 Utilizând metoda join(), formați un singur șir prin concatenarea elementelor listei C4
= ['Uzina ', 'Dacia ', 'din ', 'Pitești'].
R7.8.7
>>>C4 = ['Uzina ', 'Dacia ', 'din ', 'Pitești']
>>>s = "".join(C4)
>>>print(s)
Uzina Dacia din Pitești
7.8.8 Să se obțină o listă de cuvinte prin separarea șirului s = "maglev inseamna magnetic
levitation".
R7.8.8
>>>s = "maglev inseamna magnetic levitation"
>>>cuvinte = s.split()
142 Șiruri
>>>print(cuvinte)
['maglev', 'inseamna', 'magnetic', 'levitation']
7.8.9 Metoda strip() a obiectelor șir se utilizează pentru eliminarea unor caractere precizate,
din fața și de la sfârșitul șirurilor, sintaxa fiind următoarea:
sir.strip([caractere])
R7.8.9
>>>s1 = '%%%sir_test1%%%
>>>print(s1.strip('%%%'))
sir_test1
>>>s2 = ' sir_test2 '
>>>print(s2.strip(' '))
sir_test2
Output
abra-brabra-cabra-da
Pentru a vedea mai clar efectul metodei join(), se poate rula secvența:
b = ['a', 'bra', 'ca', 'da', 'bra']
s = '#'.join(b)
print(s)
Output
a#bra#ca#da#bra
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 8
Output
{1, 2, 3}
{1.0, 'program', (1, 2, 3)}
Output
{1, 2, 3, 4}
{1, 2, 3}
Traceback (most recent call last):
. . . . . . . .
xset = {1, 2, [3, 4]}
TypeError: unhashable type: 'list'
Crearea unui set vid se poate face printr-un artificiu, după cum se descrie în continuare.
144 Seturi
Prin utilizarea acoladelor {} se va produce un dicționar vid. Pentru a face un set fără membri
se utilizează funcția set() fără argumente.
Exemplul 8.3 # Crearea unui set vid
# initializarea lui a cu {} (dicționar vid)
a = {}
# verificarea tipului de data a variabilei a
print(type(a))
# initializarea lui a cu set()
a = set()
# verificarea tipulu de data a lui a
print(type(a))
print(a)
Output
<class 'dict'>
<class 'set'>
set()
Output
{1, 3}
{1, 2, 3}
{1, 2, 3, 4}
{1, 2, 3, 4, 5, 6, 8}
TypeError: 'set' object does not support indexing
145 Seturi
Output
{1, 3, 4, 5, 6}
{1, 3, 5, 6}
{1, 3, 5}
{1, 3, 5}
Traceback (most recent call last):
File "<string>", line 28, in <module>
KeyError: 2
Asemănător, se poate elimina și întoarce un element din set utilizând metoda pop().
Deoarece setul nu este ordonat, elementul eliminat va fi complet arbitrar, nu poate fi precizat.
De asemenea, se pot elimina toate elementele dintr-un set utilizând funcția clear().
Exemplul 8.6 #utilizare funcții pop() și clear()
# initializare xset, rezulta un set cu elemente unice
xset = set("Buna_ziua!")
print(xset)
#elementele setului sunt ordonate aleator
#ca urmare, extragerea cu functia pop() va fi corespunzatoare
#ordonarii aleatoare
print(xset.pop())
print(xset)
print(xset.pop()) # se extrage alt element
print(xset) # afisare multimea ramasa
print(xset.pop()) # se extrage alt element
print(xset) # afisare multimea ramasa
# stergere xset cu metoda clear()
xset.clear()
print(xset) #set vid
Output
{'a', 'i', 'n', '!', '_', 'u', 'B', 'z'}
146 Seturi
a
{'i', 'n', '!', '_', 'u', 'B', 'z'}
i
{'n', '!', '_', 'u', 'B', 'z'}
n
{'!', '_', 'u', 'B', 'z'}
set()
Uniunea seturilor
Definiție. Uniunea dintre A și B este mulțimea de elemente obținută din cele două mulțimi
(fig. 4.1).
Intersecția seturilor
Definiție. Intersecția dintre A și B este mulțimea comună (aparținând ambelor) mulțimilor A
și B.
147 Seturi
Diferența mulțimilor
Definiție. Diferența dintre mulțimea A și mulțimea B (notat A – B) este mulțimea elementelor
care aparțin numai lui A și nu și lui B. Asemănător, B – A este mulțimea de elemente care sunt
în B, dar nu în A.
Diferența dintre două seturi se face utilizând operatorul ( – ), sau metoda difference().
Exemplul 8.9 # Diferența dintre doua seturi utilizând metoda difference().
>>>A = {1, 2, 3, 4, 5}
>>>B = {4, 5, 6, 7, 8}
>>>print(A - B)
{1, 2, 3}
# utilizarea funcției difference(), se calculeaza A - B
>>> A.difference(B)
{1, 2, 3}
#se calculeaza B - A
>>> B - A
{8, 6, 7}
# utilizarea funcției difference(), se calculeaza A - B
>>> B.difference(A)
{8, 6, 7}
Diferența simetrică
Definiție. Diferența simetrică dintre A și B este mulțimea de elemente din A și B, dar nu în
ambele (din reuniune se exclude intersecția).
Pentru diferența simetrică se utilizează operatorul ^ , sau metoda symmetric_difference().
Exemplul 8.10 # Diferența simetrică a două seturi utilizând operatorul ^
>>>A = {1, 2, 3, 4, 5}
>>>B = {4, 5, 6, 7, 8}
>>>print(A ^ B)
{1, 2, 3, 6, 7, 8}
# Diferența simetrică a doua seturi, metoda symmetric_difference()
>>> A.symmetric_difference(B)
{1, 2, 3, 6, 7, 8}
# operația este comutativa
>>> B.symmetric_difference(A)
{1, 2, 3, 6, 7, 8}
148 Seturi
Output
True
False
Output
o g p e m r a
all() – Întoarce True dacă toate elementele setului sunt adevărate (sau dacă
setul este gol).
any() - Întoarce True dacă oricare element al setului este adevărat. Dacă
setul este gol, întoarce False.
enumerate() – Întoarce un obiect enumerare care conține indexul și valoarea
pentru toate elementele setului, ca perechi.
len() – Întoarce lungimea setului (numărul de elemente).
max() - Întoarce cel mai mare element din set.
min() - Întoarce cel mai mic element din set.
sorted() - Întoarce o nouă listă sortată obținută din elementele setului (nu
sortează setul).
sum() - Întoarce suma tuturor elementelor din set.
frozenset({1, 2, 3, 4, 5, 6})
>>> A.add(3)
...
AttributeError: 'frozenset' object has no attribute 'add'
R8.9.2 Răspuns: 10
8.9.3 Care opțiune este corectă pentru a crea o mulțime (set) vidă ?
a. set() b. () c. []
R8.9.3 Răspuns: a
8.9.4 Ce este o copie “shalow” a unui set? Exemplificați.
R8.9.4 Ca și în cazul altor obiecte, o copie “shalow” a unui set este o copie distinctă, bit cu
bit, a setului original.
xset = set(["verde","galben"])
yset = xset
xset_copie = xset.copy()
print(xset, " xset, inainte de stergere")
print(yset, " yset, inainte de stergere")
print(xset_copie, " xset_copie, inainte de stergere")
yset.clear()
print(xset, " xset, dupa stergere")
print(yset, " yset, dupa stergere")
print(xset_copie, " xset_copie, dupa stergere")
Output
{'galben', 'verde'} xset, inainte de stergere
{'galben', 'verde'} yset, inainte de stergere
{'galben', 'verde'} xset_copie, inainte de stergere
set() xset, dupa stergere
set() yset, dupa stergere
{'galben', 'verde'} xset_copie, dupa stergere
8.9.5 Să se scrie o secvență de program pentru a verifica dacă un set este un subset al altui
set. Se vor utiliza operatorul “<=” și metoda issubset().
R8.9.5
xset = set(["verde","galben"])
yset = set(["verde","rosu"])
zset = set(["verde"])
151 Seturi
Output
x: {'verde', 'galben'}
y: {'verde', 'rosu'}
z: {'verde'}
Este x un subset al lui y ? : False False
Este y un subset al lui x ? : False False
Este x un subset al lui z ? : False False
Este z un subset al lui x ? : True True
8.9.6 Să se exemplifice operațiile următoare aplicate unor seturi: maximul și minimul în set,
lungimea unui set, prezența sau absența unui element în set.
R8.9.6
xset = set([7,16, 2, 33, 21, 5])
print("Setul creat este: ", xset)
print("Elementul maxim in set este: ", max(xset))
print("Elementul minim in set este: ", min(xset))
print("Lungimea setului este: ", len(xset))
print("Test daca 9 este in set: ", 9 in xset)
print("Test daca 9 este in set: ", 9 in xset)
print("Test daca 9 nu este in set: ", 9 not in xset)
print("Test daca 33 este in set: ", 33 in xset)
print("Test daca 33 nu este in set: ", 33 not in xset)
Output
Setul creat este: {33, 2, 5, 7, 16, 21}
Elementul maxim in set este: 33
Elementul minim in set este: 2
Lungimea setului este: 6
Test daca 9 este in set: False
Test daca 9 este in set: False
Test daca 9 nu este in set: True
Test daca 33 este in set: True
Test daca 33 nu este in set: False
8.9.7 Se poate crea un set de seturi? Cum se poate rezolva?
R8.9.7
Dacă se încearcă crearea unui set de seturi se obține eroare:
>>>{{4,5}, {8,9}}
. . . . . . . .
TypeError: unhashable type: 'set'
>>>set([{4,5}, {8,9}])
. . . . . . . .
TypeError: unhashable type: 'set'
O posibilă rezolvare este utilizarea frozenseturilor:
>>>{frozenset({4, 5}), frozenset({8, 9})}
{frozenset({4, 5}), frozenset({8, 9})}
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 9
Dicționare
9.1 Definiția și crearea dicționarelor
Definiție. Dicționarul este o colecție de date compusă din elemente alcătuite din perechi
formate dintr-o cheie și o valoare corespondentă, scrise sub forma (key: value).
Crearea unui dicționar se face simplu, prin plasarea elementelor între acolade.
În timp ce valoarea poate fi de orice tip de dată, cheia trebuie să fie de un tip imutabil (șir,
număr, tuplu cu elemente imutabile, sau frozenset).
Exemplul 9.1 # dicționarul vid
xdict = {}
print(xdict)
xdict = {1: 'rosu', 2: 'negru'} #dictionarul cu cheie intreaga
xdict = {'nume': 'Alex', 1: [12, 4, 3]} #dictionarul cu chei diverse
Output
{}
{1: 'rosu', 2: 'negru'}
{'nume': 'Alex', 1: [12, 4, 3]}
{1: 'rosu', 2: 'negru'}
{1: 'tenis', 2: 'baschet'}
Output
Mihai
25csa 9
None
153 Dicționare
Output
{'nume': 'Mihai', 'varsta': 42}
{'nume': 'Mihai', 'varsta': 42, 'telefon': '0740879331'}
Output
16
{1: 1, 2: 4, 3: 9, 5: 25}
(5, 25)
{1: 1, 2: 4, 3: 9}
{}
Traceback (most recent call last):
. . . . . . . . . . .
print(patrate) #afisare eroare (dictionarul nu mai exista)
NameError: name 'patrate' is not defined
154 Dicționare
Tabelul 9.1
Output
{'Fizica': 0, 'Chimie': 0, 'Informatica': 0}
('Fizica', 10)
('Chimie', 9)
('Informatica', 8)
['Chimie', 'Fizica', 'Informatica']
Output
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
Secvența de instrucțiuni de mai sus este echivalentă cu:
patrate = {}
for x in range(6):
patrate[x] = x*x
print(patrate)
O comprehensiune de dicționar poate conține opțional mai multe instrucțiuni for sau if .
O instrucțiune if poate filtra suplimentar elementele noului dicționar creat.
Observație. Construcția conținând if se numește comprehensiune cu gardă.
Exemplul 9.7 # Comprehensiune de dicționar utilizând instrucțiunea if (cu gardă)
patrate_impare = {x: x*x for x in range(11) if x % 2 == 1}
print(patrate_impare)
Output
{1: 1, 3: 9, 5: 25, 7: 49, 9: 81}
Se poate testa dacă o cheie este utilizată într-un dicționar folosind in. Acest lucru este valabil
doar pentru chei, nu și pentru valori.
Exemplul 9.8 # Test de apartenență sau neapartenență la dicționar pentru chei
patrate = {1: 1, 3: 9, 5: 25, 7: 49, 9: 81}
print(1 in patrate) #True
print(2 not in patrate) #True
print(49 in patrate) #test pentru valori => False
Output
True
True
False
Output
1
9
25
49
81
Pentru dicționare se folosesc frecvent funcții built-in: all(), any(), len(), cmp(),
sorted(), etc.
Tabelul 9.2
all() - Întoarce True dacă toate cheile din dicționar sunt True (sau dacă
dicționarul este gol).
any() - Întoarce True dacă o cheie din dictionar este true. Dacă
dicționarul este gol se întoarce False.
len() - Întoarce lungimea dicționarului (numărul de elemente).
cmp() - Compară elementele a două dicționare (indisponibil în Python 3)
sorted() - Întoarce o listă sortată a cheilor din dicționar.
Output
False
True
6
[0, 1, 3, 5, 7, 9]
Output
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
9.8.2 În legătură cu cheile dicționarului, care afirmație nu e adevărată:
a. nu este permisă mai mult de o aceeași cheie (chei multiple)
b. datele (valorile) sunt imutabile
c. cheile trebuie să fie întregi.
R9.8.2 răspuns: c
157 Dicționare
R9.8.3 c
9.8.4 Să se simuleze procesul vânzării cărților într-o librărie, utilizând un dicționar.
R9.8.4 Se creează un inventar al cărților din magazin, sub forma unui dicționar. Cheia
dicționarului va fi câmpul nume autor, iar valoarea va fi o listă conținând numărul de exemplare
disponibile și prețul cărții.
carti = {"Eminescu Mihai": [5, 70],
"Creanga Ion": [5, 50],
"Minulescu Ion": [6, 45]}
for cheie, valoare in carti.items():
print(cheie, '-', valoare[0], '-', valoare[1])
cost = 0
while True:
carte = input("Autor carte ? (s = stop)=")
if carte == 's':
break
cantit = int(input("nr. cerut?="))
if cantit > carti[carte][1]:
print("Neacceptat - depasire stoc!")
continue
cost += carti[carte][0]*cantit
carti[carte][0] -= cantit
print("Pret:", cost)
for cheie, valoare in carti.items():
print(cheie, 'stoc:', valoare[0], 'pret:', valoare[1])
Output
Eminescu Mihai - 5 - 70
Creanga Ion - 5 - 50
Minulescu Ion - 6 - 45
Autor carte ? (s = stop)=Eminescu Mihai
nr. cerut?=3
Autor carte ? (s = stop)=s
Pret: 15
Eminescu Mihai stoc: 2 pret: 70
Creanga Ion stoc: 5 pret: 50
Minulescu Ion stoc: 6 pret: 45
R9.8.6 keys() întoarce cheile, values() întoarce valorile și items() întoarce termenii
dicționarului (perechile cheie-valoare).
xdict = {'i':5, 'j':8, 'k':34}
print(xdict.keys())
dict_keys(['i', 'j', 'k'])
print(xdict.values())
dict_values([5, 8, 34])
print(xdict.items())
dict_items([('i', 5), ('j', 8), ('k', 34)])
9.8.7 Să se scrie o secvență de iterare pentru dicționarul:
xdict = {'x':10, 'y':20, 'z':30}
R9.8.7
Metoda 1
>>>for cheie, valoare in xdict.items():
... print(cheie, valoare)
x 10
y 20
z 30
Metoda 2
>>>for cheie in xdict:
...print(cheie, xdict[cheie])
x 10
y 20
z 30
Metoda 3
>>>print([cheie for cheie in xdict.items()])
[('x', 10), ('y', 20), ('z', 30)]
9.8.8 Să se obțină un dicționar din următoarele două liste, prima corespunzând cheilor, iar a
doua corespunzând valorilor:
lista1 = ['x', 'y', 'z', 'w']
lista2 = [6, 12, 9, 15]
R9.8.8
Metoda 1
lista1 = ['x', 'y', 'z', 'w']
lista2 = [6, 12, 9, 15]
dict_12 = {}
for k in range(len(lista1)):
dict_12[lista1[k]] = lista2[k]
print(dict_12)
Output
{'x': 6, 'y': 12, 'z': 9, 'w': 15}
Metoda 2
# se utilizeaza functia zip() care produce un obiect iterator tuplu prin
#imperecherea elementelor din alti doi iteratori
lista1 = ['x', 'y', 'z', 'w']
lista2 = [6, 12, 9, 15]
dict_12 = dict(zip(lista1, lista2))
159 Dicționare
print(dict_12)
Output
{'x': 6, 'y': 12, 'z': 9, 'w': 15}
9.8.9 Să se calculeze suma și media aritmetică a tuturor valorilor din următorul dicționar:
Xdict = {'x1': 6, 'x2': 12, 'x3': 9, 'x4': 15}
R9.8.9
xdict = {'x1': 6, 'x2': 12, 'x3': 9, 'x4': 15}
suma = sum(xdict.values())
print("Suma tuturor valorilor din dictionar =", suma)
n = len(xdict)
print("Numarul perechilor din dictionar n=", n)
print("Media aritmetica = suma/n = ", suma/n)
Output
Suma tuturor valorilor din dictionar = 42
Numarul perechilor din dictionar n= 4
Media aritmetica = suma/n = 10.5
9.8.10 Să se combine două dicționare într-unul singur, adunând valorile pentru cheile comune.
Cele două dicționare sunt:
xdict = {"a":23, "b":41, "c":22}
ydict = {"b":99, "c":38, "d":7, "e":59}
Output
Counter({'b': 140, 'c': 60, 'e': 59, 'a': 23, 'd': 7})
R9.8.11 4
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 10
În modul binar, din fișier se obține informația sub formă binară, în octeți. Acest mod este
utilizat pentru preluarea conținutului fișierelor non-text, ca de exemplu fișierele cu imagini, sau
cod executabil.
Modurile posibile de deschidere sunt: r, w, a, x, t, b, +. Semnificația lor este redată
mai jos.
r - Deschiderea unui fișier pentru citire (implicit)
w - Deschiderea unui fișier pentru scriere. Dacă fișierul nu există, se creează unul nou,
iar dacă există, acesta va fi trunchiat (va fi șters conținutul său).
x - Deschidere doar (exclusiv) pentru creare. Dacă fișierul există deja, operația eșuează.
a - Deschide un fișier pentru adăugare de noi date la sfârșitul său. Dacă fișierul nu există,
se creează unul nou.
t - Deschidere în mod text (implicit)
b - Deschidere în mod binar
+ - Deschidere fișier pentru actualizare (citire și scriere)
Codarea textului. Un alt parametru necesar pentru o bună utilizare a comenzii open este
encoding. Precizarea tipului de codare pentru modul text de deschidere este foarte
162 Operații I/O. Fișiere
Pentru a afla tipul de codare al sistemului de operare sub care se lucrează, se pot utilza
comenzile:
>>>import sys
>>>sys.getdefaultencoding() #Windows 11
'utf-8'
Metoda de mai sus nu prezintă suficientă siguranță. Dacă o excepție are loc în timpul
operațiunilor cu fișierul, programul este părăsit și fișierul poate rămâne deschis.
O cale mai sigură este utilizarea blocului try … finally.
Exemplul 10.4 #includerea operațiilor cu fișiere în blocul try … finally
try:
f = open("exemplu.txt", encoding = 'utf-8')
# diverse operații cu datele fișierului
finally:
f.close()
Această cale asigură închiderea fișierului chiar dacă are loc o excepție care ar putea opri rularea
programului.
Observație. Cea mai bună cale de a închide un fișier este utilizarea instrucțiunii with. Aceasta
garantează că fișierul este închis când blocul intern instrucțiunii with este părăsit. În acest caz
nu mai este necesară apelarea explicită a metodei close(), care se face intern.
Exemplul 10.5 #sintaxa și utilizarea funcției with
with open("exemplu.txt", encoding = 'utf-8') as f:
# diverse operații cu datele fișierului
O atenție mai specială trebuie avută în modul w , deoarece se poate suprascrie conținutul
fișierului, dacă acest conținut există deja.
Scrierea unui șir, sau a unei secvențe de octeți (în cazul fișierelor binare) se face cu metoda
write(). Metoda întoarce și numărul de caractere scrise în fișier.
Această secvență de cod va crea un nou fișier cu numele exemplu.txt dacă acesta nu există în
directorul curent de lucru. Dacă există, se va suprascrie.
Pentru citire, un fișier trebuie deschis în modul r. Se pot aplica mai multe procedee pentru
aceasta.
Prima variantă utilizează metoda read(size) pentru a citi un număr size de caractere. Dacă
parametrul size nu este specificat, se citește până la sfârșitul fișierului, când întoarce eof (end-
of-file).
Exemplul 10.7 #citirea fișierului exemplu.txt scris în secțiunea anterioară.
>>>f = open("exemplu.txt",'r',encoding = 'utf-8')
>>>f.read(6) # citirea primelor 6 caractere
'Titlul'
>>>f.read(9) # se citesc încă 9 caractere
' lucrării'
>>>f.read() # se citeste restul de caractere pana la eof (end of file)
'\n\nPrimul rând al textului\nAl doilea rând al textului\n. . . . . .
. . . .\nAl n - lea rând al textului\n'
>>>f.read() # mai departe citirea întoarce siruri vide
''
Se poate vedea că metoda read() întoarce o linie nouă marcată cu '\n'. După atingerea eof,
se obțin prin citire numai șiruri goale.
Observație. Se poate schimba poziția cursorului în fișier utilizând metoda seek().
Observație. Asemănător, metoda tell() întoarce poziția curentă a cursorului în fișier
(numărul de caractere/octeți față de început).
Exemplul 10.8 #poziționarea în fișier
>>>f.tell() # obține poziția curentă in fisier
136
>>>f.seek(0) # poziționarea cursorului pe zero
0
>>>print(f.read()) # read the entire file
Titlul lucrării
Primul rând al textului
164 Operații I/O. Fișiere
A doua variantă permite citirea rapidă și eficientă un fișier text linie cu linie folosind o buclă
for.
In fișierul txt, liniile sale includ caracterul linie nouă (newline \n), astfel că în funcția print()
nu a mai fost necesar să se prevadă acest simbol, pentru ca listarea să fie corect aliniată.
A treia variantă permite citirea individuală a liniilor fișierului prin utilizarea metodei
readline(). Această metodă citește fișierul până la caracterul newline (inclusiv).
Toate cele trei procedee întorc valori vide când se atinge sfârșitul fișierului (eof).
fisier = open("matcon.txt" )
dir(fisier)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__',
'__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__', '_checkClosed',
'_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing',
'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno',
'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read',
'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable',
'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
close() - Închide un fișier deschis. Nu are efect dacă fișierul este deja închis.
detach() - Separă bufferul binar din TextIOBase și îl returnează.
fileno() - Întoarce un număr întreg, descriptorul de fișier
flush() - Eliberează bufferul de scriere din memorie al fluxului fișier (îl trimite în disc)
isatty() - Întoarce True dacă fluxul fișier este interactiv.
read(n) - Citește până la n charactere din fișier. Citește până la sfârșitul fișierului dacă n este
negativ sau None.
readable() - Întoarce True dacă fluxul fișier poate fi citit.
readline(n = -1) - Citește și întoarce o linie din fișier. Citește până la n octeți dacă este
specificat.
readlines(n = -1) - Citește și întoarce o listă de linii din fișier. Citește cel mult n linii dacă este
specificat.
seek(offset, from = SEEK_SET) - Schimbă poziția cursorului în fișier cu offset octeți, în
raport cu from
seekable() - Întoarce True dacă fluxul fișier permite acces aleator.
tell() - Întoarce poziția cursorului în fișier.
truncate(size = None)- Redimensionează fluxul fișier la size octeți. Dacă size nu este
specificat, redimensionează locația curentă.
writable() - Întoarce True dacă fluxul fișier poate fi scris
write(s) - Scrie șirul s în fișier și întoarce numărul de caractere scrise.
writelines(lines) - Scrie o listă de linii în fișier.
Definiție. Un director sau un folder (dosar) este o colecție de fișiere și subdirectoare. Modulul
os conține multe metode utile pentru lucrul cu directoare și fișiere.
Observație. Se poate obține numele directorului de lucru curent utilizând metoda getcwd()
(get current working directory), din modulul os.Numele întors este sub formă de șir. De
asemenea, pentru obținerea numelui ca obiect binar se poate utiliza medoda getcwdb().
Exemplul 10.12
>>> import os
>>> os.getcwd()
'C:\\RoboDK\\Python37'
>>> print(os.getcwd())
C:\RoboDK\Python37
'.gitconfig',
'.glue',
'.idea',
'.idlerc',
'.ipynb_checkpoints',
'.ipython',
'.jupyter',
'.keras',
'.kivy',
'.matplotlib',
'.openjfx',
'.pylint.d',
'.spyder-py3',
. . . . . . . . . . . . . .
>>> os.listdir('C:\\')
[['$AV_ASW',
'$Recycle.Bin',
'$WinREAgent',
'AX NF ZZ',
'Blender',
'BMW M3 Challenge',
'Brother',
'Config.Msi',
'Documents and Settings',
'DRIVERS',
'DroidCam',
. . . . . . . . . . . . . . .
Observație. Deoarece nu se poate utiliza metoda rmdir() pentru ștergerea directoarelor care
nu sunt vide, se poate face apel la metoda rmtree(). Aceasta poate fi importată din modulul
shutil.
Exemplul 10.18 #ștergere director ne-vid cu rmtree()
>>> os.listdir()
['test']
>>> os.rmdir('test')
Traceback (most recent call last):
...
OSError: [WinError 145] The directory is not empty: 'test'
>>> import shutil
>>> shutil.rmtree('test')
>>> os.listdir()
[]
Output
Urmeaza scrierea in stdout
start
inainte
dreapta
stop
[. . . . . . 'iesire.txt' . . . . . . .]
Afisare continut iesire.txt
start
inainte
dreapta
stop
R10.8.1
Mod r r+ w w+ a a+ x x+
Operație
read
write
creare
ștergere
Poziție inițială s s e e s s
(s-start, e-end)
f_mc.close()
Output
Creare si scriere fisier matcon.txt
Fisierul matcon.txt a fost creat si inchis
R10.8.3
nume_fis = "matcon.txt"
Tlinii = Tcuvinte = Tcaractere = 0
flag = 'unu'
for caract in i_linie:
if (caract != ' ')|(caract != "\t") and flag == 'unu':
Tcuvinte += 1
flag = 'zero'
elif (caract == ' ')|(caract == "\t"):
flag = 'unu'
Output
Total Linii: 5
Total Cuvinte 45
Total Caractere 202
10.8.4 Să se citească fișierul “matcon.txt” sub forma unei liste, eliminând caracterele “\n” și
înlocuind caracterele “\t” cu patru spații.
R10.8.4
t_f = open("matcon.txt")
t_txt = t_f.read()
t_f.close
print("Continut initial")
print(repr(t_txt))
print()
l_txt = t_txt.split("\n")
#print(l_txt)
nl_txt = ' '.join(l_txt)
print()
print("Continut fara nl")
print(repr(nl_txt))
tab_txt = nl_txt.split("\t")
# print(tab_txt)
sp_txt = ' '.join(tab_txt)
print()
print("Cantinut fara nl si tab")
print(repr(sp_txt))
sp_f.close()
Output
Continut initial
'BL1\tBara lemn patrata\t 4\t4x4\t25\nBL4\tBara lemn patrata\t
5\t8x8\t60\nTM5\tTeava metalica patrata 2mm\t6\t4x4\t100\nTM6\tTeava
metalica patrata 4mm\t6\t6x6\t250\nBL9\tBara lemn dreptunghiulara
\t8\t8x15\t150\n'
Continut fara nl
'BL1\tBara lemn patrata\t 4\t4x4\t25 BL4\tBara lemn patrata\t
5\t8x8\t60 TM5\tTeava metalica patrata 2mm\t6\t4x4\t100 TM6\tTeava
metalica patrata 4mm\t6\t6x6\t250 BL9\tBara lemn dreptunghiulara
\t8\t8x15\t150 '
Capitolul 11
Alte exemple: automobil, contabil, procesul 25, meniul principal, Mihai, etc.
Definiție. O clasă este o descriere a unei mulțimi de obiecte cu proprietăți (atribute) similare,
comportament similar (operații similare) și relații similare față de alte obiecte. O clasă este un
model pentru un tip de obiecte (abstractizează o mulțime de obiecte). Exemple de clasă:
vehicul, funcționar, proces, meniu, persoană, etc.
Observație. Nu trebuie să se confunde clasa cu obiectul. Dacă există o clasă persoană, atunci
George, Vasile sunt obiecte din aceeași clasă, denumite instanțe ale clasei persoană. Obiectul
moștenește toate caracteristicile clasei din care face parte.
11.2 Clasele
O clasă poate fi gândită ca un șablon după care se construiește un obiect (o descriere, sau o
declarație). Pentru clase nu se alocă memorie.
Exemplul 11.2 #clasa automobil
Ca ilustrare a noțiunii de clasă se poate oferi modelul sau schița unui automobil, compusă din
etichete care conțin detalii despre nume, culoare, dimensiuni, etc. Pe baza acestei descrieri, se
poate cunoaște ce este și cum funcționează obiectul automobil.
Sintaxa clasei automobil este:
class automobil:
pass
Pentru definirea unei clase vide s-a utilizat cuvântul cheie class .
Dintr-o clasă se poate crea un obiect specific denumit instanță. Altfel spus, implementările
particularizate ale claselor sunt denumite instanțe. În consecință, un obiect este o instanță
a unei clase.
11.3 Obiectele
Definiție. Un obiect (instanța) este o instanțiere a unei clase. Spre deosebire de clase, pentru
obiecte se alocă memorie.
Exemplul 11.3 #obiect instanțiat din clasa automobil
obj = automobil()
Prin instrucțiunea de mai sus, obj este un obiect creat plecând de la clasa automobil.
Dacă se dispune de amănuntele descriptive și comportamentale ale noțiunii de automobil
(atributele), se pot construi clasa și obiectele de tip automobil.
Exemplul 11.4 #Crearea claselor și obiectelor automobil
class automobil:
# atributul
categoria = "vehicul"
#functia de initializare
def __init__(self, marca, nume, an):
self.marca = marca
self.nume = nume
self.an = an
175 Programarea Orientată pe Obiecte – prezentare generală
Output
Focus este un vehicul
Si Duster este un vehicul
Focus an de fabricatie 2013
Duster an de fabricatie 2016
De notat că în programul de mai sus sunt două feluri de atribute ale clasei.
Atributul categoria este specific clasei și are aceeași valoare pentru toate instanțele clasei. De
aceea este denumit atribut al clasei și poate fi apelat și înainte de instanțierea clasei (înainte
de crearea obiectelor).
Atributele marca, nume, an vor lua valori specifice instanței, ele fiind inițializate în momentul
în care se creează instanța (obiectul) în prin metoda __init__ , care se execută prima. Acestea
sunt atribute specifice obiectelor.
Ford și Dacia sunt referințe la obiectele noi create.
Toate obiectele noi vor include aceeași valoare pentru atributul categoria care nu poate fi
modificat la crearea unui nou obiect, dar atributele marca, nume, an vor putea fi modificate
de fiecare dată când se creează un nou obiect.
11.4 Metodele
Funcțiile definite în corpul unei clase sunt denumite metode. Scopul lor este impunerea unui
anumit comportament obiectului.
Exemplul 11.5 #Crearea clasei și obiectelor automobil
class automobil:
# atributele
categoria = "vehicule"
# constructorul
def __init__(self, marca, nume, an):
self.marca = marca
self.nume = nume
self.an = an
#metodele
def cumpara(self, pret):
return "{} cumparare {} lei".format(self.nume, pret)
def vinde(self, pret):
return "{} vinzare {} lei".format(self.nume, pret)
#instantierea obiectului
auto1 = automobil("Dacia", "Duster", 2013)
176 Programarea Orientată pe Obiecte – prezentare generală
Output
Duster cumparare 10000 lei
Focus vinzare 7000 lei
În programul de mai sus s-au definit metodele cumpara() și vinde(). Ele au fost apelate numai
după ce au fost create obiectele auto1 și auto2 (instanțe ale clasei automobil). De aceea se
numesc și metode ale unor obiecte (ale unor instanțe).
11.7.1 Abstractizarea
Definiție. Abstractizarea este o caracteristică care evidențiază aspectele esențiale ale unei
entități, eventual neglijând (filtrând) unele proprietăți mai puțin importante sau care nu
interesează în contextul aplicației. În cazul unui obiect se precizează ce este și ce trebuie să
facă acel obiect.
Fără a abstractiza sau filtra proprietățile în exces ale obiectelor, ar fi greu să se proceseze
multitudinea de informații existente și să se reușească focalizarea pe sarcina de îndeplinit.
Ca urmare a abstractizării, când două persoane diferite interacționează cu același obiect, se
ocupă adesea fiecare cu un subset diferit de atribute. De exemplu, când conduce automobilul,
șoferul trebuie să știe viteza mașinii și direcția pe care merge. Deoarece mașina are un sistem
automat de comandă, el nu trebuie să cunoască turația sau cuplul motorului, așa că va filtra
aceste informații. Pe de altă parte, aceste informații ar fi esențiale pentru un șofer pilot de curse,
care nu le-ar filtra.
Atunci când se construiesc obiecte în aplicațiile OOP, este important să se utilizeze conceptul
de abstractizare. De exemplu, în cazul unei aplicații de transport, se poate proiecta un produs
(obiect) cu atribute precum mărimea și greutatea. Culoarea produsului ar fi o informație
excedentară și va fi eliminată. Pe de altă parte, la proiectarea unei aplicații de introducere date
referitoare la comenzi, culoarea ar putea fi importantă și va fi inclusă ca atribut al produsului
obiect.
11.7.2 Moştenirea
Majoritatea obiectelor sunt clasificate pe baza ierarhiilor.
Ierarhiile se pot constitui pe baza proprietăților sau funcțiunilor. De exemplu, vehiculele se pot
clasifica în grupuri ca având anumite caracteristici comune, cum ar fi numărul de roți și
sistemul de propulsie. Grupurile se clasifică în continuare în subgrupuri cu atribute comune,
cum ar fi culoarea și dimensiunea, etc. De asemenea, o altă clasificare a vehiculelor se poate
face în raport de funcția lor. De exemplu, există vehicule comerciale de mărfuri (camioane,
autobasculante, etc.) și vehicule de pasageri (autoturisme, autobuze, etc.).
Definiție. Moștenirea este transmiterea atributelor și operațiilor într-o ierarhie de clase, din
clasă în clasă.
Moștenirea simplifică programarea deoarece se permite atribuirea caracteristicilor generale
unui obiect părinte și moștenirea acestor caracteristici în obiectul ierarhic descendent (copil).
Exemplul 11.7 #ilustrarea moștenirii
Se poate defini un obiect “salariat” care definește toate caracteristicile generale ale salariților
dintr-o firmă. Se poate defini apoi un obiect “șef_formație” care moștenește caracteristicile
obiectului salariat, dar adaugă și caracteristici unice pentru șefii de formații din firmă. Obiectul
șef_formație va reflecta automat orice modificare în implementarea obiectului salariat.
Moștenirea este o cale de creare a unei noi clase utilizând o clasă existentă deja. Noua clasă
beneficiază de toate detaliile proprii vechii clase, care nu se modifică. Se spune că noua clasă
(copil, sau child) este derivată din vechea clasă (clasa părinte sau clasa de bază).
Exemplul 11.8 #utilizarea moștenirii
# clasa parinte (de baza)
class Vehicul:
def __init__(self):
print("vehiculele transportă ceva")
179 Programarea Orientată pe Obiecte – prezentare generală
def identif(self):
print("Vehicul")
def miscare(self):
print("Vehiculul se mișcă")
# clasa copil (derivata)
class Avion(Vehicul): #considerand avionul un vehicul aerian
def __init__(self):
# apelează functia super()
super().__init__()
print("Avionul zboară")
def identif(self):
print("Avion")
def zboara(self):
print("zborul este mai rapid")
Tarom_237 = Avion()
Tarom_237.identif()
Tarom_237.miscare()
Tarom_237.zboara()
Output
vehiculele transportă ceva
Avionul zboară
Avion
Vehiculul se mișcă
zborul este mai rapid
În programul de mai sus s-au creat două clase: Vehicul (clasa părinte sau de bază) și Avion
(clasa copil sau derivată). Clasa copil moștenește funcțiile clasei părinte. Acest lucru este
dovedit prin metoda miscare().
Clasa copil modifică comportamentul clasei părinte, acest lucru fiind dovedit de metoda
identif(). De asemenea, clasa copil extinde funcțiunile clasei părinte prin crearea metodei
zboara().
Unul dintre conceptele de bază ale POO este restricționarea accesului la metode și variabile,
cu scopul evitării alterării lor nedorite. Pentru a pune în practică acest concept este utilizată
metoda încapsulării variabilelor și metodelor.
Definiție. A încapsula o metodă sau variabilă înseamnă de fapt a restricționa accesul la acestea
(denumite private), spre deosebire de cazul celor publice.
Observație. Atributele private sunt diferențiate față de cele publice prin utilizarea in Python a
prefixelor underscore simplu ( _ ) și dublu ( __ ).
Exemplul 11.9 # încapsularea datelor in Python
class minge_tenis:
def __init__(self):
self.__pretmax = 5
def vinde(self):
print("Pret de vanzare: {}".format(self.__pretmax))
def pune_pretmax(self, pret):
self.__pretmax = pret
c = minge_tenis()
c.vinde()
# incercare de schimbare de pret, care
# va esua deoarece nu se poate schimba variabila privata
c.__pretmax = 4
c.vinde()
# utilizează funcția de stabilire a pretului, va functiona
c.pune_pretmax(4)
c.vinde()
Output
Pret de vanzare: 5
Pret de vanzare: 5
Pret de vanzare: 4
obiectul poate spune apoi care este metoda de implementat în funcție de contextul mesajului
(cu alte cuvinte, funcție de numărul și tipul de argumente).
Definiție. În programare, polimorfism înseamnă că o funcție poate fi folosită cu argumente
care la momentul apelării pot fi de fiecare dată de tipuri diferite, eventual în număr diferit.
Observație. Din perspectiva POO, polimorfismul înseamnă utilizarea aceleiași interfețe cu
tipuri diferite de date (forme).
Un exemplu clasic de polimorfism în programare este cel al unei metode/funcții de colorare
prin care se pot colora cu diferite culori diferite forme (cerc, patrat, triunghi, etc.).
Exemplul 11.11 # o funcție polimorfică “built-in”
# len() se poate utiliza pentru un sir
print(len("dimensiune"))
# len() se poate utiliza pentru o lista
print(len([10, 20, 30]))
Output
10
3
Un alt exemplu de polimorfism este utilizarea metodelor cu același nume pentru clase diferite.
Mai jos se prezintă un program în care apar două clase utilizate în același mod. Din fiecare
clasă se construiește cîte un obiect. O buclă for parcurge cele două obiecte. Apoi se apelează
metodele care există cu același nume în fiecare clasă.
Exemplul 11.12 # metode cu același nume pentru clase diferite
class Fac_Inginerie():
def specializari(self):
print("Electronica, Mecanica, Automatica")
def durata(self):
print("Licenta 4 ani, master 2 ani")
def exam_admitere(self):
print("Examen de admitere la matematica, fizica")
class Fac_Medicina():
def specializari(self):
print("MGenerala, Stomatologie, Pediatrie")
def durata(self):
print("Durata studii 6 ani")
def exam_admitere(self):
print("Examen de admitere la anatomie, chimie")
Ing = Fac_Inginerie()
Med = Fac_Medicina()
for facultate in (Ing, Med):
facultate.specializari()
facultate.durata()
facultate.exam_admitere()
Output
Electronica, Mecanica, Automatica
Licenta 4 ani, master 2 ani
Examen de admitere la matematica, fizica
MGenerala, Stomatologie, Pediatrie
Durata studii 6 ani
Examen de admitere la anatomie, chimie
182 Programarea Orientată pe Obiecte – prezentare generală
Suprascrierea metodelor
Polimorfismul poate fi și în strânsă legătură cu conceptul de moștenire. Polimorfismul oferă
posibilitatea definirii de metode în clasa copil, la care se atribuie același nume ca în clasa
părinte.
Prin moștenire, clasa copil moștenește metodele din clasa părinte. Totuși, este posibil să se
modifice o metodă din clasa copil care este moștenită din clasa părinte. Acest lucru este dorit
când metoda moștenită nu corespunde integral necesităților din clasa copil.
Se spune că, în acest caz, se re-implementează metoda în cadrul clasei copil. Procesul de re-
implementare a unei metode în clasa copil se numește “suprascrierea metodei” (method
overrriding).
Exemplul 11.13 #Suprascrierea unei metode
class Vehicul:
def general(self):
print("Cele mai multe vehicule se misca pe roti")
def miscare(self):
print("Miscarea rotilor deplaseaza vehiculul")
class Elicopter(Vehicul):
def miscare(self):
print("Elicopterul zboara datorita rotirii elicei")
class Vapor(Vehicul):
def miscare(self):
print("Vaporul se deplaseaza plutind pe apa")
ob_vehicul = Vehicul()
ob_elicopt = Elicopter()
ob_vapor = Vapor()
ob_vehicul.general()
ob_vehicul.miscare()
ob_elicopt.general()
ob_elicopt.miscare()
ob_vapor.general()
ob_vapor.miscare()
Output
Cele mai multe vehicule se misca pe roti
Miscarea rotilor deplaseaza vehiculul
Cele mai multe vehicule se misca pe roti
Elicopterul zboara datorita rotirii elicei
Cele mai multe vehicule se misca pe roti
Vaporul se deplaseaza plutind pe apa
Observație. POO face ca programele să fie mai ușor de înțeles și mai eficiente. Prin
abstracțiunea datelor, acestea devin mai sigure și mai protejate. Polimorfismul permite aceeași
interfață pentru diferite obiecte, astfel că programatorii pot să scrie cod mai eficient. Codul
poate fi reutilizat.
POO PP
1 Calculul se face prin intermediul Calculul se face pe baza unei liste de
obiectelor. instrucțiuni, pas cu pas.
2 Dezvoltarea și întreținereaÎn PP întreținerea programelor devine dificilă
programelor este mai ușoară și costisitoare dacă programele se măresc
3 POO simulează entități din lumea Nu se simulează lumea reală, rezolvarea
reală. Rezolvarea problemelor apare problemelor se face prin algoritmi abstracți.
mai concretă și mai simplă Aceștia sunt implementați pas cu pas, în
secvențe denumite funcții.
4 POO mărește securitatea datelor În PP datele pot fi accesate cu ușurință de
(date protejate și private) oriunde.
5 POO încurajează reutizabilitatea și PP nu are echivalent pentru conceptul de
scurtarea codului prin mecanisme ca moștenire
moștenirea
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 12
Output
Se va crea/construi/instantia un obiect cu numele Test
185 Clase și obiecte în Python
Output
<class '__main__.Persoana'>
Mihai
Clasa contine datele unei persoane
<function Persoana.salut at 0x00000258D1342678>
Buna ziua Mihai!
În exemplul de mai sus, după crearea obiectului clasă, s-a arătat că acesta poate fi utilizat pentru
accesarea diferitelor sale atribute.
De asemenea, obiectul clasă poate fi utilizat pentru crearea de noi obiecte (instanțe) ale clasei.
În program, această acțiune este foarte asemănătoare cu apelul funcțiilor.
>>> Pers2 = Persoana() #Un nou obiect
>>> print(Pers2.salut())
În secvența de mai sus s-a instanțiat un obiect denumit Pers2. Apoi s-a accesat un atribut al
acestuia folosindu-i numele ca prefix.
Atributele pot fi date sau metode. Metodele obiectului instanțiat corespund funcțiilor clasei
respective.
Observație. În clase se vorbește de “funcții”, în obiectele care sunt instanțe ale clasei
corespondente denumirea este de “metode”.
Atributele definite în cadrul clasei sunt denumite “atributele clasei”, iar atributele definite în
cadrul funcțiilor sunt denumite “atributele instanței”. Atributele clasei pot fi accesate de clasa
însăși (nume_clasă.nume_atribut), ca și de instanțele clasei (instanță.nume_atribut). Astfel,
instanțele au acces la ambele atribute: ale clasei și ale instanței.
Un atribut al clasei poate fi suprascris într-o instanță, dar nu este o metodă bună (de a utiliza
încapsularea).
Parametrul self din definiția funcției din interiorul clasei este asemănător pointerului THIS
din limbajul C++ și trebuie să fie prezent obligatoriu (def salut(self)).
Totuși, la apelul metodei obiectului, parametrul self se poate omite și se poate scrie
Pers1.salut(). Acest lucru este posibil deoarece la apelul metodei, această referire la însuși
obiectul este făcută implicit, astfel încât Pers1.salut() se transformă în
Persoana.salut(self).
186 Clase și obiecte în Python
În general, la apelul unei metode cu o listă de n argumente, acesta se transformă într-un apel
echivalent la o funcție cu o listă de n+1 argumente prin inserția unui argument (self) referind
obiectul. Acest lucru nu se mai întâmplă dacă primul argument are denumirea self.
În concluzie, primul argument al funcției în cadrul clasei trebuie să fie însuși obiectul. Acesta
este denumit convențional self.
Observație. Se poate utiliza o denumire diferită de self, dar se recomandă cu insistență
păstrarea convenției de denumire self.
Observație. Atributele (înțelegând atât atributele propriu-zise, cât și funcțiile/metodele unei
clase/obiect) se pot obține în Python cu comanda dir(nume_clasă/obiect). Explicitarea
faptului că un nume din lista obținută este atribut se face cu comanda
hasattr(nume_clasă/object,"atribut/method").
De asemenea lista de atribute a unui modul se poate face tot cu comanda dir(nume_modul),
iar specificarea faptului ca un modul are un anumit atribut se face cu comanda
hasattr(module_name, "attr_name").
>>>hasattr(math, "log")
True
>>>dir(Pers1)
187 Clase și obiecte în Python
Se constată că s-a obținut o precizare utilă doar în cazul metodei (că atributul este o metodă).
Autodocumentarea claselor. Primul șir în interiorul unei clase este denumit docstring și
reprezintă o scurtă descriere a clasei. Deși nu este obligatoriu, este recomandat ca prezența
docstring să devină o regulă.
Exemplul 12.4 #clasa și docstringul
class Xclasa:
'''Acesta este un docstring. Rolul sau
este sa descrie Xclasa'''
pass
Instrucțiunea del poate fi utilizată și pentru ștergerea atributelor unui obiect, dar nu este
întotdeauna posibil, unele atribute fiind read-only (pot fi doar citite).
Exemplul 12.6 #nu este posibilă ștergerea atributului
>>> c1 = complex(3,1)
188 Clase și obiecte în Python
>>> print(c1.imag)
1.0
del(c1.imag)
Traceback (most recent call last):
. . . . . . . . . . . .
del(c1.imag)
AttributeError: readonly attribute
Output
2 3
inainte de stergerea atributului obiectului A1
A1 contine x? True
A1 contine y? True
Traceback (most recent call last):
. . . . . . . . . . . .
print(A1.x, A1.y)
AttributeError: 'A' object has no attribute 'x'
De asemenea, atributele unor obiecte se mai pot șterge prin funcția built-in delattr().
Output
Nume: Tudor
Varsta: 20
În programul de mai sus, persNume și persVarsta sunt membri publici ai datelor, iar metoda
afisare_varsta() este o funcție membru public al clasei persoana. Acești membri date ai
clasei persoana pot fi accesați de oriunde în program.
B. Modificator de acces protejat
Membrii unei clase care sunt declarați protejați sunt accesibili numai unei clase derivate din
aceasta. Membrii date ai unei clase sunt declarați protejați prin adăugarea unui singur simbol
de subliniere (underscore) “_” înaintea numelor.
Exemplul 12.9 #access protejat la date și metode intr-o clasă
class Salariat: # clasa parinte
_nume = None # data protejata
_marca = None # data protejata
_sectia = None # data protejata
def __init__(self, nume, marca, sectia):
self._nume = nume
self._marca = marca
self._sectia = sectia
def _afisare_marca_sectia(self): # functie protejata
# accessare date protejate
print("Marca: ", self._marca)
print("Sectia: ", self._sectia)
Output
190 Clase și obiecte în Python
Nume: Tudor
Marca: 12345
Sectia: Sectia motoare
În programul de mai sus, _nume, _marca și _sectia sunt membri date protejați, iar metoda
__afisare_marca_sectia() este o metodă protejată a super-clasei Salariat. Metoda
afisare_detalii() este o funcție publică membră a clasei persoana care este derivată din
clasa Salariat, metoda afisare_detalii din clasa persoana accesează membrii de date
protejați ai clasei Salariat.
C. Modificator de acces privat
Membrii unei clase care sunt declarați privați sunt accesibili numai în cadrul clasei.
Modificatorul de acces privat este cel mai sigur modificator de acces. Membrii date ai unei
clase sunt declarați privați prin adăugarea unui simbol dublu de subliniere (underscore) “__”
înaintea numelui datei din acea clasă.
Exemplul 12.10 #access privat la date și metode intr-o clasă
class persoana:
__nume = None #date private
__marca = None #date private
__sectia = None #date private
def __init__(self, nume, marca, sectia):
self.__nume = nume
self.__marca = marca
self.__sectia = sectia
def __afisare_detalii(self): #functie privata
# acces la date private
print("Nume: ", self.__nume)
print("Marca: ", self.__marca)
print("Sectia: ", self.__sectia)
def func_acces_privat(self): #functie publica
self.__afisare_detalii() #acces la functie privata
# creare obiect
ob_pers = persoana("Tudor", 12345, "Sectia motoare")
# apelul functiei publice
ob_pers.func_acces_privat()
Output
Nume: Tudor
Marca: 12345
Sectia: Sectia motoare
În programul de mai sus, __nume, __marca și __sectia sunt membri date privați, metoda
__ afisare_detalii este o funcție membru privat (acestea pot fi accesate numai în cadrul
clasei) și metoda func_acces_privat() este o funcție publică membru a clasei persoana care
poate fi accesată de oriunde în cadrul programului. Metoda func_acces_privat accesează
membrii privați ai clasei persoana.
12.4 Moștenirea
Definiție. Moștenirea se referă la crearea unei noi clase pornind de la o clasă existentă care nu
se modifică. Clasa nouă se numește clasă copil (sau derivată), iar clasa de la care moștenește
se numește clasă părinte (sau de bază).
Sintaxa
191 Clase și obiecte în Python
class clasaBaza:
Corpul clasei de baza/părinte
class clasaDerivata(clasaBaza):
Corpul clasei derivate
Clasa derivată moștenește caracteristicile clasei de bază, iar alte noi caracteristici sunt adăugate
la cele moștenite, în noua clasă. Practic, apare avantajul reutilizării codului deja scris.
Exemplul 12.8 #moștenirea figurilor geometrice
Se definește poligonul ca fiind o figură geometrică formată din 3 sau mai multe segmente de
dreaptă (laturi) care sunt unite și formează un contur închis.
Clasa denumită Poligon este prezentată în programul de mai jos. Atributele clasei sunt
numărul de laturi n, și o listă cu dimensiunile acestora, denumită laturi.
Metoda input_laturi() citește mărimea fiecărei laturi și metoda afiseaza_latura(self)
imprimă lungimea fiecărei laturi.
Se creează o clasă denumită Triunghi (poligon cu trei laturi) care moștenește clasa Poligon.
În consecință, toate atributele clasei Poligon devin disponibile clasei Triunghi, prezentată de
asemenea în program.
Noua metodă calcAria() a clasei Triunghi calculează și imprimă aria triunghiului.
#datele introduse trebuie sa satisfaca conditia matematica
#necesara pentru a fi laturile unui triunghi (!)
class Poligon:
def __init__(self, nr_laturi):
self.n = nr_laturi
self.laturi = [0 for i in range(nr_laturi)]
def input_laturi(self):
self.laturi = [float(input("Introdu latura "+str(i+1)+" : ")) for i
in range(self.n)]
def afiseaza_latura(self):
for i in range(self.n):
print("Latura",i+1," : ",self.laturi[i])
class Triunghi(Poligon):
def __init__(self):
Poligon.__init__(self,3)
def calcAria(self):
a, b, c = self.laturi
# calculează semi-perimetrul
s = (a + b + c) / 2
aria = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('Aria triunghiului este %0.2f' %aria)
t = Triunghi()
t.input_laturi()
t.afiseaza_latura()
t.calcAria()
Output
Introdu latura 1 : 6
Introdu latura 2 : 2
Introdu latura 3 : 5
Latura 1 : 6.0
Latura 2 : 2.0
Latura 3 : 5.0
Aria triunghiului este 4.68
192 Clase și obiecte în Python
Funcția isinstance() întoarce True dacă obiectul este o instanță a clasei sau altă clasă
derivată din ea. Toate clasele din Python moștenesc clasa de bază object.
Exemplul 12.9 #Verificarea calității de instanță a clasei, sau de clasă derivată
# t este o instanță a claei triunghi, creată în exemplul anterior
>>> isinstance(t,Triunghi)
True
>>> isinstance(t,Poligon)
True
>>> isinstance(t,int)
False
>>> isinstance(t,object)#object este clasa de baza de prim nivel in Python
True
pass
class Baza_B:
pass
class Multi_Derivata(Baza_A, Baza_B):
pass
Clasa Multi_Derivata este derivată din clasele Baza_A și Baza_B și moștenește atribute din
ambele clase.
Aici, clasa Derivata1 moștenește clasa Baza, iar clasa Derivata2 moștenește clasa Derivata1.
Output
True
True
True
În cazul unei secvențe de cod care presupune moștenire multiplă, atributele se caută mai întâi
în clasa curentă. Dacă acestea nu sunt găsite, se continuă căutarea în clasele părinte de același
nivel, în ordine de la stânga la dreapta, evitând căutarea repetată în aceeași clasă.
În exemplul anterior referitor la clasa Multi_Derivata, ordinea căutării este:
Multi_Derivata, Baza_A, Baza_B, object. Această ordonare este denumită liniarizarea
clasei Multi_Derivata, iar setul de reguli utilizate pentru a o determina se numește Ordinea
Apelării (Rezolvării) Metodei, Method Resolution Order (MRO).
MRO trebuie să garanteze că o clasă se situează întotdeauna înaintea părinților în ce privește
căutarea metodelor în vederea apelării. În cazul părinților multipli, ordinea este aceeași ca în
tuplul claselor de bază.
194 Clase și obiecte în Python
MRO al unei clase poate fi vizualizat ca un atribut cu numele __mro__ , sau ca metoda mro().
Atributul întoarce un tuplu, iar metoda întoarce o listă.
Exemplul 12.14 #afișare ordine de apelare folosind atributul __mro__ , sau metoda mro()
>>> Multi_Derivata.__mro__
(__main__.Multi_Derivata, __main__.Baza_A, __main__.Baza_B, object)
>>> MultiDerivata.mro()
[__main__.Multi_Derivata, __main__.Baza_A, __main__.Baza_B, object]
Output
Traceback (most recent call last):
. . .
print(p1+p2)
TypeError: unsupported operand type(s) for +: 'Punct' and 'Punct'
Rezultatul rulării secvenței de program de mai sus este ridicarea excepției TypeError,
deoarece, în acest moment, Python nu știe să adune sau să facă alte operații cu obiecte de tip
Punct. Totuși este posibil ca Python să poată îndeplini acest lucru prin supraîncărcarea
operatorilor.
În vederea dezvoltării acestui subiect, trebuie să se cunoască unele aspecte despre funcțiile
speciale.
12.9.2 Supraîncărcarea utilizând funcții speciale
Funcțiile (din clase) care încep cu dublu underscore ( __ ) sunt denumite funcții speciale în
Python, deoarece diferă de funcțiile tipice. De exemplu, funcția__init__() este una dintre
195 Clase și obiecte în Python
funcțiile speciale. O caracteristică ce o face specială este faptul că este apelată de fiecare dată
când se creează un nou obiect din acea clasă.
Există multe alte funcții speciale: __del__, __str__, __format__, __hash__, __bool__,
etc.
Prin utilizarea funcțiilor speciale, se poate face clasa definită anterior (Punct) compatibilă cu
funcțiile built-in.
Dacă se dorește să se imprime cu funcția print() coordonatele unui obiect Punct, se obține
un eșec, după cum se vede în exemplul următor.
Exemplul 12.16
>>>p1 = Punct(2,3)
>>>print(p1)
Output
<__main__.Punct object at 0x000001DBC16F1
Pentru rezolvarea problemei, se poate defini o metodă __str__() în clasa Punct pentru
controlul modului de printare. După cum urmează, dacă acum se reîncearcă execuția funcției
print(), se obține ce se dorește.
Output
(2, 3)
Răspunsul este cel așteptat. Se poate incerca acum invocarea funcțiilor str() sau format().
În acest caz, utilizând str(p1) sau format(p1), se apelează intern metoda p1.__str__().
Exemplul 12.18 #apelarea internă a metodei __str__()
>>>str(p1)
'(2, 3)'
>>>format(p1)
'(2, 3)'
Pentru a obține rezultatul operației Punct(p1 + p2), mai trebuie făcut ceva, după cum se va
vedea în secțiunea următoare.
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x, self.y)
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Point(x, y)
p1 = Punct(4, 5)
p2 = Punct(3, 6)
print(p1+p2)
Output
(7,11)
Când argumentul funcției este p1 + p2, se face apelul p1.__add__(p2) care la rândul său
este transformat în Punct.__add_(p1,p2). După aceasta, operația de adunare contiunuă în
modul specificat.
Ca exemplu suplimentar se prezintă în continuare supraîncărcarea unui operator de comparație
( < ), cu ilustrare pentru cazul clasei Punct.
Exemplul 12.20 # supraincarcarea operatorului “mai mic” (<)
class Punct:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x, self.y)
def __lt__(self, alt):
self_modul = (self.x ** 2) + (self.y ** 2)
alt_modul = (alt.x ** 2) + (alt.y ** 2)
return self_modul < alt_modul
p1 = Punct(1,1)
p2 = Punct(-2,-3)
p3 = Punct(1,-1)
# utilizarea operatorului supraincarcat (<)
print(p1<p2)
print(p2<p3)
print(p1<p3)
Output
True
False
False
Împărțire p1 / p2 p1.__truediv__(p2)
Împ. cu rot. infer. p1 // p2 p1.__floordiv__(p2)
Rest (modulo) p1 % p2 p1.__mod__(p2)
Bitwise Left Shift p1 << p2 p1.__lshift__(p2)
Bitwise Right Shift p1 >> p2 p1.__rshift__(p2)
Bitwise AND p1 & p2 p1.__and__(p2)
Bitwise OR p1 | p2 p1.__or__(p2)
Bitwise XOR p1 ^ p2 p1.__xor__(p2)
Bitwise NOT ~p1 p1.__invert__()
Mai mic p1 < p2 p1.__lt__(p2)
Mai mic sau egal p1 <= p2 p1.__le__(p2)
Egal p1 == p2 p1.__eq__(p2)
Not egal p1 != p2 p1.__ne__(p2)
Mai mare p1 > p2 p1.__gt__(p2)
Mai mare sau egal p1 >= p2 p1.__ge__(p2)
În exemplul de mai sus , marca este un atribut al clasei Salariat. Când se creează un nou
obiect Salariat, valoarea corespunzătoare atributului marca se mărește cu 1.
Dacă se accesează atributul marca după crearea obiectului, se obține:
>>> electrician = Salariat()
>>> Salariat.marca
1
instalator = Salariat()
>>> Salariat.marca
2
>>>electrician.varsta
32
12.10.4 Care sunt atributele built-in ale unei clase ? Oferiți un exemplu de utilizare.
R12.10.4 Orice clasă din Python are următoarele atribute built-in care pot fi accesate utilizând
operatorul punct, asemănător oricărui alt atribut:
__dict__ = dicționar conținând spațiul numelor de clasă
__doc__ = șir de documentare, dacă există
__name__ = numele clasei
__module__= numele modulului în care este definită clasa. În modul interactiv, acest atribut
este __main__
__bases__ = lista claselor de bază, ordonate, sau nimic
class Salariat:
'clasa de baza'
marcaSalariat = 0 #numarul de salariati
def __init__(self, nume, salariu):
self.nume = nume
self.salariu = salariu
Salariat.marcaSalariat +=1
def afisareMarca(self):
print("Nr. total de salariati: %d" % Salariat.marcaSalariat)
def afisareSalariat(self):
print("Nume:", self.nume, "Salariu:", self.salariu)
muncitor = Salariat("Tudor", 5000)
Salariat.afisareSalariat(muncitor)
print()
print("Salariat.__doc__:", Salariat.__doc__)
print("Salariat.__name__:", Salariat.__name__)
print("Salariat.__module__:", Salariat.__module__)
print("Salariat.__bases__:", Salariat.__bases__)
print("Salariat.__dict__:", Salariat.__dict__)
print()
print("muncitor.__doc__:", muncitor.__doc__)
#print("muncitor.__name__:", muncitor.__name__) #eroare no attribute
print("muncitor.__module__:", muncitor.__module__)
#print("muncitor.__bases__:", muncitor.__bases__)#eroare no attribute
print("muncitor.__dict__:", muncitor.__dict__)
Output
Nume: Tudor Salariu: 5000
Salariat.__doc__: clasa de baza
Salariat.__name__: Salariat
Salariat.__module__: __main__
Salariat.__bases__: (<class 'object'>,)
200 Clase și obiecte în Python
optiune = 1
while optiune != 0:
print("0.Terminare 1.Adaugare 2.Stergere 3.Afisare lista")
print()
optiune = int(input("Optiune="))
if optiune == 1:
a1 = input("Adaugati un articol:")
ob.adauga(a1)
print("Continut lista:", ob.afiseaza())
elif optiune == 2:
a2 = input("Stergeti un articol:")
ob.sterge(a2)
print("Continut lista:", ob.afiseaza())
elif optiune == 3:
print("Continut lista:", ob.afiseaza())
elif optiune == 0:
print("Terminat")
else:
print("Optiune gresita")
print("Stop program")
Output
0.Terminare 1.Adaugare 2.Stergere 3.Afisare lista
Optiune=1
Adaugati un articol:mere
Continut lista: ['mere']
0.Terminare 1.Adaugare 2.Stergere 3.Afisare lista
Optiune=1
Adaugati un articol:pere
Continut lista: ['mere', 'pere']
201 Clase și obiecte în Python
Optiune=1
Adaugati un articol:cirese
Continut lista: ['mere', 'pere', 'cirese']
0.Terminare 1.Adaugare 2.Stergere 3.Afisare lista
Optiune=3
Continut lista: ['mere', 'pere', 'cirese']
0.Terminare 1.Adaugare 2.Stergere 3.Afisare lista
Optiune=2
Stergeti un articol:pere
Continut lista: ['mere', 'cirese']
0.Terminare 1.Adaugare 2.Stergere 3.Afisare lista
Optiune=3
Continut lista: ['mere', 'cirese']
0.Terminare 1.Adaugare 2.Stergere 3.Afisare lista
Optiune=0
Terminat
Stop program
12.10.6
Practic, ce se întîmplă în cazul moștenirii fără polimorfism?
R12.10.6 Dacă nu se utilizează polimorfismul, va fi necesară o verificare suplimentară a tipului
obiectului pentru a determina metoda corectă ce va fi apelată. Acest lucru înseamnă un cod mai
mare de scris.
12.10.7 Să se scrie un program care să inverseze ordinea cuvintelor unui șir (alcătuit din mai
multe cuvinte).
R12.10.7 Se utilizează succesiv funcțiile split(), reversed() și join().
class inversare_sir:
def inversare_cuvant(self, s):
return ' '.join(reversed(s.split()))
print(inversare_sir().inversare_cuvant("Curtea de Arges"))
Output
Arges de Curtea
date_publice = None
#date membre protejate
_date_protejate = None
#date membre private
__date_private = None
#initializare date la creare obiect
def __init__(self, varpub, varprot, varpriv):
self.date_publice = varpub
self._date_protejate = varprot
self.__date_private = varpriv
#functie membra publica
def afisareMembriPublici(self): #acces la date publice
print("Date publice: ", self.date_publice)
#functie membra protejata
def _afisareMembriProtejati(self): #acces la date protejate
print("Date protejate: ", self._date_protejate)
#functie membra privata
def __afisareMembriPrivati(self): #acces la date private
print("Date private: ", self.__date_private)
#clasa derivata
class derivata(Baza):
#initializare date la creare obiect
def __init__(self, varpub, varprot, varpriv):
Baza.__init__(self, varpub, varprot, varpriv)
#functie publica
def accesMembriProtejati(self):
#acces la functia protejata a clasei de baza
self._afisareMembriProtejati()
obj.accesMembriProtejati()
#obiectul derivat apeleaza o functie publica proprie membra
#si poate accesa date protejate in clasa Baza
print("Objectul acceseaza date protejate:", obj._date_protejate)
obj.accesMembriPrivati()
# objectul apeleaza o functie publica a clasei Baza, dar nu poate accesa
#datele private, rezulta eroare de Atribut
#print(obj.__date_private)
Output
Date publice: Robot Lego
Date protejate: 1600
Date private: Mainstorms
Objectul acceseaza date protejate: 1600
R12.10.9 Metodele magice (magic methods) sunt funcții de tip dunder: __init__(sel, alte
arg.), __abs__(self), __round__(self, n), etc. Sunt apelabile indirect (transparent), de
exemplu apelarea operatorului + conduce la apelul metodei sale __add__(self, alte arg).
Denumirea dunder provine de la “double underscore”.
>>>var_nr = 20
>>>var_nr + 10
30
>>>var_nr.__add__(10)
30
12.10.10 Să se scrie un program pentru realizarea unui calculator “științific” care să utilizeze
moștenirea multiplă. Clasele părinte sunt o clasă pentru calcule aritmetice și o clasă pentru
funcții matematice (de exemplu, funcția putere).
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 13
Iteratori
13.1 Definiția și utilizarea iteratorilor
Concept. Iterabilii sunt colecții, în timp ce un iterator gestionează doar iterația (adică păstrează
poziția), dar de regulă nu este o colecție în sine.
Definiție. Iteratorii sunt obiecte pe baza cărora se poate parcurge un număr de pași într-o buclă
de procesare iterativă. Iteratorul în Python este pur și simplu un obiect care va returna date,
câte un element, pe rând.
Iteratorii sunt frecvent întâlniți într-un limbaj de programare. Ei sunt elegant implementați în
cadrul buclei for, comprehensiuni, generatoare, etc., uneori nefiind clar vizibili.
Diferența dintre un iterabil și un iterator este importantă, de exemplu, atunci când cineva
dorește să parcurgă un iterabil (printr-o o secvență de iterații) de mai multe ori. Dacă un iterator
a ajuns la finalul iterabilului, atunci a doua parcurgere nu va mai funcționa, deoarece iteratorul
a fost deja folosit și a ridicat imediat excepția StopIteration .
Observație. Iteratorii în Python nu pot fi resetați. Excepția StopIteration nu poate fi anulată
pentru un iterator, odată ce a apărut. Soluția este construcția unui nou iterator (sau generator).
Din punct de vedere tehnic, în Python, un iterator trebuie să implementeze două metode
speciale, __iter__() și __next__(), ambele făcând parte din protocolul iterator.
Un obiect este iterabil dacă se poate găsi un iterator pentru el. Multe containere built-in din
Python ca: liste, tuple, șiruri, etc., sunt iterabile. Pentru ele, funcția iter() (care la rândul său
apelează metoda specială __iter__()) întoarce un iterator.
În modul pas cu pas (“manual”), pentru utilizarea unui iterator cu un iterabil, se va utiliza
funcția next()pentru a itera prin toți termenii iterabilului. Când se va atinge sfârșitul și nu vor
mai fi date de întors, se va ridica excepția StopIteration.
Există obiecte care sunt atât iterabile (conțin metoda __iter__()) cât și iteratori (conțin
metoda __next__()) . Astfel de obiecte (fișiere, dicționare, etc.) suportă o singură iterație la
un moment dat, fiind propriul lor iterator. Listele, în schimb, sunt doar iterabile, permițand
multiple iterații simultan, cu obiecte iterator diferite.
Exemplul 13.1 #iterarea unei liste
lista = [21, 8, 9, 33] #se definește iterabilul: o listă
iterator = iter(lista) #se obține un iterator cu functia iter()
print(next(iterator)) # se iterează “manual” aplicând next()
print(next(iterator))
print(iterator.__next__()) # next(obj) echivalent cu obj.__next__()
print(iterator.__next__())
next(iterator) # Eroare, nu mai sunt termeni
Output
21
8
9
33
Traceback (most recent call last):
205 Iteratori
. . .
next(iterator) # Eroare, nu mai sunt termeni
StopIteration
O cale mai elegantă, pentru iterarea “automată”, este utilizarea buclei for. Cu ajutorul acesteia,
se poate itera orice obiect care întoarce un iterator (liste, șiruri, fișiere, etc.).
Exemplul 13.2 #iterarea unei liste utilizând for
>>> for termen in lista:
... print(termen)
...
Output
21
8
9
33
Altfel spus, un iterator nu este altceva decât un obiect container care implementează protocolul
iterator (care este bazat pe cele două metode: __next__, care întoarce următorul element al
containerului și __iter__, care întoarce iteratorul însuși).
Exemplul 13.3 #iterator creat pentru un șir utilizând funcția built-in iter
>>> i = iter('abc')
>>> next(i)
'a'
>>> next(i)
'b'
>>> next(i)
'c'
>>> next(i)
Traceback (most recent call last):
. . . . . . .
next(i)
StopIteration
Bucla for este în realitate implementată ca o buclă while infinită, prin care trec toate
excepțiile, în afară de StopIteration. În interiorul buclei for se creează un obiect iterator,
ob_iterator, prin apelarea funcției iter() pe iterabil. Următorul termen al iterabilului se
obține apelând next() și apoi se execută corpul buclei for cu această valoare. După ce toți
termenii iterabilului se epuizează se ridică excepția StopIteration care este prinsă intern și
bucla se închide.
Implementarea se face după următorul model:
Exemplul 13.4 #funcționarea reală - implementarea buclei for printr-un while
206 Iteratori
Output
1
2
4
8
16
Output
functia fy face ceva
functia fy face ceva
functia fy face ceva
Un iterator de tip infinit se poate implementa și în cadrul unei clase.
Exemplul 13.7 #iterator infinit implementat printr-o clasă
class it_infinit:
""" iterator care va întoarce
toate numerele impare """
208 Iteratori
def __iter__(self):
self.num = 1
return self
def __next__(self):
num = self.num
self.num += 2
return num
Output
>>> a = iter(it_infinit())
>>> next(a)
1
>>> next(a)
3
>>> next(a)
5
>>> next(a)
7
Se poate continua la infinit cu apelul next(a).
Observație. La utilizarea unui iterator infinit, întotdeauna trebuie acordată atenție includerii
unei condiții de terminare.
Concluzie. Construcția și utilizarea iteratorilor prezintă avantajul economisirii de resurse. De
exemplu, în exemplul de mai sus, se pot obține numerele impare fără a se stoca în memorie
toate numerele posibile. Teoretic, se poate avea într-o memorie finită un număr infinit de
numere.
R13.6.1
Sintaxa: *zip(iterator1, iterator2, iterator3 ...) *
an = (1998, 2004, 2012, 2019)
luna = ("mar", "iun", "ian", "dec")
zi = (11, 21, 13, 5)
print(tuple(zip(an, luna, zi)))
Output
[(1998, 'mar', 11), (2004, 'iun', 21), (2012, 'ian', 13), (2019, 'dec', 5)]
13.6.2 Să se scrie un program Python prin care se creează un nou iterator din mai mulți iterabili
și să se afișeze tipul acestuia.
Output
Tipul noului iterator:
<class 'itertools.chain'>
Elementele noului iterator:
1 2 3 a1 a2 a3 a4 4 5 6 7 8 9
Tipul noului iterator:
<class 'itertools.chain'>
Elementele noului iterator:
11 22 33 44 A B C D 55 66 77 88 99
13.6.3 Să se scrie o secvență de program pentru generarea unui ciclu infinit de elemente
dintr-un iterabil.
R13.6.3 Se utilizează funcția cycle() din modulul bibliotecă itertools.
Soluția 1
import itertools as it
def cicl_infinit(itr):
return it.cycle(itr)
# rulează la infinit
#Elemente din Lista
210 Iteratori
noul_iterator = cicl_infinit(['A','B','C','D'])
print("Număr infinit de elemente generate:")
for i in noul_iterator:
print(i, ' ', end = '')
Output
Număr infinit de elemente generate:
A B C D A B C D A B C D A B C D A B C D A B C D A
B C D A B C D A B C D A B C D A B C D . . . . . . . . .
Soluția 2
import itertools as it
def cicl_infinit(itr):
return it.cycle(itr)
# rulează la infinit
#Elemente dintr-un sir
noul_iterator = cicl_infinit('Limbajul Python')
print("Număr infinit de elemente generate:")
for i in noul_iterator:
print(i, sep=' ', end = '')
Output
Număr infinit de elemente generate:
Limbajul PythonLimbajul PythonLimbajul PythonLimbajul PythonLimbajul
PythonLimbajul PythonLimbajul PythonLimbajul PythonLimbajul . . . . . . . .
. . . . . . . . . . . . . . . . . .
13.6.4 Să se scrie un program pentru a genera toate permutările posibile între n obiecte.
R13.6.4 Se utilizează funcția permutation() din modulul bibliotecă itertools.
import itertools as it
def permutari(elem):
for elemente in it.permutations(elem):
print(elemente)
permutari([1])
print("\n")
permutari([1,2])
print("\n")
permutari([1,2,3])
print("\n")
permutari(['a','b','c'])
Output
(1,)
(1, 2)
(2, 1)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
13.6.5 Să se scrie o secvență de program Python care să găsească lungimea maximă a unui
subșir care face parte dintr-un șir și care este format din caractere identice. Se vor utiliza funcții
din modulul itertools.
R13.6.5 Se utilizează funcția groupby().
import itertools as it
def max_subsir(sir):
return max(len(list(x)) for _, x in it.groupby(sir))
sir = "TRVHHHHHmg?dgfIIIfnfnvvvvvvppdAFt?gmmmm"
print("Sirul initial:",sir)
print("Lungimea maximă a subsirului format\
din caractere identice:", max_subsir(sir))
Output
Sirul initial: TRVHHHHHmg?dgfIIIfnfnvvvvvvppdAFt?gmmmm
Lungimea maximă a subsirului formatdin caractere identice: 6
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 14
Generatoare
14.1 Definiția și crearea generatoarelor
Necesitatea funcțiilor generatoare. În practica programării poate apărea necesitatea
prelucrării unui volum mare de date aflate în structuri de tipul: liste, dicționare, tablouri, baze
de date, etc. Prelucrarea prin metode clasice poate întâmpina dificultăți datorită consumului
mare de memorie deoarece prin stocarea atât a articolelor cât și a indecșilor aferenți acestora
în memoria calculatorului, aceasta se poate ocupa rapid și poate deveni astfel insuficientă. O
utilizare mai eficientă a memoriei se poate face dacă se substituie memorarea, de exemplu, a
unei întregi liste cu indicii aferenți, ce ar urma să fie prelucrată ca lot, cu memorarea și
prelucrarea individuală a unui singur articol la un moment dat. În acest caz, trebuie să se rezolve
problema producerii “întârziate” a indecșilor (iteratorului).
Metoda clasică de a implementa un iterator constă în utilizarea metodelor __iter__() și
__next__() cu urmărirea stărilor interne ale structurii (secvență de program, clasă) create și
la final ridicarea excepției StopIteration în momentul în care s-au epuizat valorile. Cantitatea
de cod ce trebuie scrisă este însă suficient de mare ca să descurajeze programarea în acest stil,
în plus ceea ce se poate realiza nu este ceva chiar foarte elegant și intuitiv.
O soluție de ieșire din acest impas este utilizarea funcțiilor generatoare, sau mai scurt spus,
utilizarea generatoarelor. Prin intermediul lor se pot crea iteratori în mod simplu și rapid. O
funcție generatoare conține și administrează tot ceea ce corespunde metodei clasice a unui
iterator.
Definiție. O generatoare este o funcție care întoarce un obiect (o valoare unică a unui iterator)
care poate fi utilizat pentru iterare (câte o valoare la un moment dat).
Crearea unei generatoare este foarte simplă, aceasta definindu-se ca o funcție obișnuită, dar
care utilizează cuvântul cheie yield în locul înstrucțiunii return.
Observație. Pentru a defini o generatoare, funcția trebuie să conțină cel puțin o declarație
yield, dar funcția poate să mai conțină și alte declarații yield și chiar return.
Observație. Modul de funcționare al instrucțiunilor return și yield este diferit prin aceea că
în timp ce return termină definitiv funcția, cu posibilitatea întoarcerii unei valori,
instrucțiunea yield produce o pauză în execuția funcției, salvează toate stările acesteia și poate
continua din acel punct, la un nou apel al funcției care poate apărea în continuare.
Mai jos sunt prezentate diferențele între o funcție normală și o funcție generatoare.
1. O funcție generatoare conține una sau mai multe instrucțiuni yield.
2. După apel, funcția generatoare întoarce un obiect (un element al iteratorului), dar nu
reîncepe execuția imediat (ci doar după ce se face activarea cu metoda next()).
3. Funcția generatoare implementează automat metodele __iter__() și __next__(). În
consecință se vor itera termenii iterabilului cu funcția next().
4. Odată ce funcția generatoare produce (“yields”) un obiect, ea este pusă pe “pauză“, iar
controlul este transferat apelantului.
5. Între apelurile succesive ale generatoarei, variabilele locale și stările lor sunt memorate (în
același spațiu limitat de memorie).
213 Generatoare
6. După terminarea la final a execuției funcției generatoare, dacă mai urmează alte apeluri
ale acesteia, se ridică excepția StopIteration.
Exemplul 14.1 # O funcție generatoare (cea mai simplă), cu mai multe instrucțiuni yield
>>>def fgen():
... n = 1
... print('Pasul 1')
... yield n
... n += 1
... print('Pasul 2')
... yield n
... n += 1
... print('Pasul 3')
... yield n
...
>>>j = fgen()
>>>next(j)
Pasul 1
1
>>>next(j)
Pasul 2
2
>>>next(j)
Pasul 3
3
>>>next(j)
Traceback (most recent call last):
. . .
next(j)
StopIteration
n += 1
print('Pasul 2')
yield n
n += 1
print('Pasul 3')
yield n
for fiecare_termen in fgen():
print(fiecare_termen)
Output
Pasul 1
1
Pasul 2
2
Pasul 3
3
Exemplul de mai sus este prezentat doar pentru ilustrarea a ceea ce se întâmplă pe fond, dar
lucrurile sunt ceva mai complexe.
În mod normal, o funcție generatoare se implementează cu o buclă for având și o condiție de
terminare a iterațiilor.
Exemplul 14.3 #inversarea unui șir cu generatoare cu for și condiție de terminare
#indexul buclei for se obtine cu functia range()
def inversare_sir(sir_A):
lungime = len(sir_A)
for i in range(lungime - 1, -1, -1):
yield sir_A[i]
for litera in inversare_sir("abcdef"):
print(litera)
Output
f
e
d
c
b
a
Observație. Funcția generatoare de mai sus poate lucra și cu alte tipuri de iterabile (liste,
tupluri, etc.)
Exemplul 14.4 #funcție generatoare cu buclă for pentru obținere numere pare
def nr_pare(x):
while(x!=0):
if x%2==0:
yield x
x-=1
for i in nr_pare(8):
print(i)
Output
8
6
4
2
215 Generatoare
Output
[25, 9, 49, 196]
<generator object <genexpr> at 0x00000256DF4276C8>
Rezultatul execuției arată că expresia generatoare produce un obiect generator care nu oferă
rezultatul dorit imediat, ci numai după ce i se cere (“la cerere”), asa cum se poate vedea în
exemplul care urmează.
Output
8
64
216
512
Traceback (most recent call last):
. . .
next(ob_g)
216 Generatoare
StopIteration
Observație. Expresiile generatoare pot fi folosite ca argumente ale funcțiilor. În acest caz, se
renunță la parantezele rotunde.
Exemplul 14.7 #argumentul funcției este o expresie generatoare
>>> sum(x**3 for x in lista)
800
>>> max(x**5 for x in lista)
32768
Output
1
32
>>> i=iter({1,3,2})
>>> type(i)
<class ‘set_iterator’>
10. Un generator este un iterator. Generatoarele sunt o subclasă a Iteratoarelor. Acest lucru se
poate verifica folosind funcția issubclass().
Exemplul 14.10
Observație. Deși, așa cum s-a arătat mai sus, o generatoare este un iterator, reciproca nu este
întotdeauna adevărată (nu orice iterator este o generatoare).
11. Iteratorul este el însuși un iterabil. Iteratorul este o subclasă a clasei iterabilelor ( Iterable).
>>> issubclass(collections.Iterator,collections.Iterable)
True
12. Implementarea utilizând funcția generatoare poate fi mai scurtă și mai clară deoarece
funcția generatoare poate automatiza și ascunde detaliile procesului iterativ.
13. Generatoarele sunt soluții potrivite pentru reprezentarea fluxurilor infinite de date.
Fluxurile infinite de date nu pot fi reprezentate în memorie și din acest motiv, generatoarele
care pot produce doar un element la un moment de timp, pot reprezenta un flux infinit de date.
218 Generatoare
14. Generatoarele pot fi utilizate pentru asamblarea operațiilor după modelul “benzii rulante”
(pipeline), model întâlnit la calculul unor serii de numere (de exemplu, seria Fibonacci).
for i in gen_index_m7():
print(i, end=" ")
Output
0 7 14 21 28 35 42 49
14.5.2 Pot fi utilizate mai multe instrucțiuni yield într-o funcție generatoare?
R14.5.2 Da, pot fi utilizate mai multe instrucțiuni yield, spre deosebire de return.
O funcție normală poate conține mai multe instrucțiuni return, dar după executarea primului
return se iese din funcție, funcția este terminată.
În cazul funcției generatoare, se pot executa mai multe instrucțiuni yield, după care se poate
încheia funcția generatoare.
def multi_yield():
sir1 = "aaaaa"
yield sir1
sir2 = "bbbbb"
yield sir2
sir3 = "ccccc"
yield sir3
ob_gen = multi_yield()
print(next(ob_gen))
print(next(ob_gen))
print(next(ob_gen))
Output
aaaaa
bbbbb
ccccc
14.5.3 Se pot folosi generatoare direct în interiorul unei funcții (în locul listei de argumente)
pentru a scrie un cod mai scurt și mai curat. Să se exemplifice la calculul unei sume folosind
un generator direct ca argument pentru funcția de calcul.
R14.5.3
>>> sum(i for i in range(10))
45
219 Generatoare
14.5.4 Să se genereze o comparație între metoda clasei iterator, respectiv metoda funcției
generatoare prin scrierea a două secvențe de program pentru calculul pătratului unui număr.
R14.5.4
Metoda clasei iterator
class P2:
def __init__(self, max=0):
self.n = 0
self.max = max
def __iter__(self):
return self
def __next__(self):
if self.n > self.max:
raise StopIteration
r = 2 ** self.n
self.n += 1
return r
a=P2(3)
print(P2().__sizeof__()) #afisare memoria alocata clasei iterator
print(a.__sizeof__()) #afisare memoria alocata obiectului iterator
print(a) # indica obiectul
i = iter(a)
# apel manual (explicit)
print(i) #inainte de next(), indica obiectul
print(next(i))
print(next(i))
print(next(i))
print(next(i))
# apel automat (buclă)
b = P2(3)
print(b) # indica obiectul
j = iter(b)
while (1000): # bucla infinită
print(next(j))
Output
32
32
<__main__.P2 object at 0x00000256DF7FE7C8>
<__main__.P2 object at 0x00000256DF7FE7C8>
1
2
4
8
<__main__.P2 object at 0x00000256E1088E08>
1
2
4
8
Traceback (most recent call last):
. . .
print(next(j))
. . .
raise StopIteration
StopIteration
n = 0
while n < max:
yield 2 ** n
n += 1
a=P2(3)
print(P2().__sizeof__()) #afisare memoria alocata funcției generatoare
print(a.__sizeof__())
print(a)
i = iter(a)
# apel manual (explicit)
print(i) #inainte de next(), indica obiectul
print(next(i))
print(next(i))
print(next(i))
# apel automat (bucla)
b = P2(3)
print(b)
j = iter(b)
while (1000): # bucla infinită
print(next(j))
Output
96
96
<generator object P2 at 0x00000256E10031C8>
<generator object P2 at 0x00000256E10031C8>
1
2
4
<generator object P2 at 0x00000256E0A391C8>
1
2
4
Traceback (most recent call last):
. . .
print(next(j))
StopIteration
14.5.5 Să se calculeze suma patratelor numerelor din seria Fibonacci folosind doua generatoare.
R14.5.5
#generatorul (I) de numere din seria Fibonacci
def nr_fibonacci(nr):
x, y = 0, 1
for _ in range(nr):
x, y = y, x+y
print(x, end = " ")
yield x
#generatorul (II) de numere ridicate la puterea a doua
def patrat(nr):
for num in nr:
yield num**2
print("\nSuma patratelor: ", sum(patrat(nr_fibonacci(8))))
print("\nSuma patratelor: ", sum(patrat(nr_fibonacci(14))))
Output
1 1 2 3 5 8 13 21
Suma patratelor: 714
1 1 2 3 5 8 13 21 34 55 89 144 233 377
Suma patratelor: 229970
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 15
Închideri
15.1 Concept
În filozofia POO, protecția prin ascunderea datelor ocupă un loc important. De regulă, acest
lucru se face în cadrul și prin construcția claselor și obiectelor. Există totuși aplicații în care
definirea de noi clase fie complică codul, fie nu se justifică din alte diverse motive, dar rămâne
totuși interesul pentru accesul protejat și controlat la anumite date.
Un exemplu de date sensibile sunt variabilele globale. Acestea pot fi alterate relativ ușor,
nedorit, în cadrul unui program. O cale de a evita utilizarea acestor variabile vulnerabile este
introducerea lor în clase și obiecte, dar astfel fie s-ar complica inutil programul, fie ar
presupune un efort prea mare. Soluția pentru astfel de cazuri este utilizarea inchiderilor
(clojure), adică închiderea într-o funcție.
Noțiunea de închidere are legătură cu noțiunea de variabilă non-locală din funcțiile încuibate
(nested). Se reamintește că o funcție încuibată în Python este o funcție declarată în cadrul altei
funcții, denumită funcție de “închidere” (enclosing function). Funcția încuibată are acces total
la variabilele definite în domeniul său de vizibilitate și doar acces parțial, numai pentru citire
(read-only), la variabilele din domeniul de vizibilitate al funcției care o închide/conține. Pentru
a avea acces total este necesară folosirea cuvântului cheie nonlocal în fața variabilei, în cadrul
funcției încuibate.
Exemplul 15.1 # acces la variabile din funcția închisă fara declaratia nonlocal
def print_msg(msg):
# functia de inchidere externa
def printeaza():
# functia incuibata (inchisa)
msg = "Salut!"
print(msg)
print(msg)
printeaza()
print(msg)
print_msg("Buna ziua!")
Output
Buna ziua!
Salut!
Buna ziua!
Output
Buna ziua!
Salut!
Salut!
Exemplul 15.3
def print_msg(msg):
# functia de inchidere externa
def imprima():
# functia încuibata
print(msg)
return imprima # intoarce functia incuibata
test_cloj = print_msg("Salut!")
test_cloj()
Output
Salut!
Rezultatul de mai sus pare puțin ciudat. Funcția print_msg()a fost apelată cu argumentul șir
"Salut!", iar funcția returnată a fost legată la numele test_cloj. Apoi, apelând
test_cloj(), se imprimă mesajul "Salut!", deși funcția print_msg() își terminase
deja execuția.
Această tehnică prin care date locale (“Salut!” în acest caz) rămân atașate de o variabilă este
ceea ce se numește “închidere (clojure)” în Python.
Valoarea din funcția de inchidere este memorată în afara domeniului său de vizibilitate. Acest
lucru se întâmplă chiar dacă funcția de inchidere este ștearsă din spațiul de nume curent, după
cum se poate vedea continuînd rularea exemplului de mai sus în consola Python.
Exemplul 15.4 (continuare)
>>> del print_msg
>>> test_cloj()
Salut!
>>> print_msg("Salut!")
Traceback (most recent call last):
. . .
print_msg("Salut!")
NameError: name 'print_msg' is not defined
Concluzia la exemplul de mai sus este: funcția întoarsă de return încă funcționează, deși funcția
originală a fost ștearsă.
223 Închideri
Observație. O închidere există numai dacă o funcție încuibată face referire la o valoare situată
în spațiul său de închidere. Criteriile asociate definiției unei închideri sunt următoarele:
- Trebuie să existe o funcție încuibată.
- Funcția încuibată trebuie să se refere la o variabilă locală.
- Funcția de închidere trebuie să întoarcă (prin return) funcția încuibată.
Output
36
21
112
Output
<function obtine_cheia.<locals>.inchide at 0x000001CB1C6B25F0>
1234
1234
1234
15.4.2 Să se implementeze o secvență de numărare prin care o variabilă ”contor” să fie protejată
prin închidere. Ce soluție permite incrementarea variabilei contor la fiecare apel al funcției de
închidere?
R15.4.2 Pentru ca funcția încuibată să poată modifica variabila contor, este necesar ca aceasta
să fie declarată ca fiind nonlocală. În acest fel se transformă dintr-o variabilă imutabilă într-
o variabilă mutabilă.
def contor(ctr):
ctr = 0
def inchis_contor():
nonlocal ctr
ctr += 1
print(ctr)
return inchis_contor
numarator = contor(0)
del contor
print(numarator)
numarator()
numarator()
numarator()
Output
<function contor.<locals>.inchis_contor at 0x000001E211A125F0>
1
2
3
15.4.3 Să se arate ambele variante de implementare, prin clase și prin închideri, ale problemei
de ascundere a volorii variabilei reprezentând suma unor încasări într-o aplicatie financiară.
R15.4.3
Implementarea cu clasă:
225 Închideri
class Total_Incasat():
def __init__(self):
self.lei = []
def __call__(self, val):
self.lei.append(val)
_lei = sum(self.lei)
return _lei
total = Total_Incasat()
s = total(int(input('Suma=')))
print('Total=',s)
s = total(int(input('Suma=')))
print('Total=',s)
s = total(int(input('Suma=')))
print('Total=',s)
s = total(int(input('Suma=')))
print('Total=',s)
Output
Suma=56
Total= 56
Suma=14
Total= 70
Suma=25
Total= 95
Suma=56
Total= 151
Output
Suma=89
Total= 89
Suma=45
Total= 134
Suma=56
Total= 190
Suma=45
Total= 235
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 16
Funcții decoratoare
16.1 Concept și definiție
Decorarea funcțiilor reprezintă un aspect al domeniului programării calculatoarelor denumit
metaprogramare. În principiu, prin metaprogramare se înțelege modificarea pe care o face o
parte a programului unei alte părți a programului, la momentul compilării (înainte de execuție).
Definiție (1). O funcție decoratoare se aplică unei alte funcții astfel încât funcția țintă să capete
una sau mai multe funcționalități suplimentare față de cele de la momentul creerii sale. Metoda
decorării unei funcții reprezintă o formă de metaprogramare.
Pentru a înțelege mai bine modul cum funcționează decoratorii, este necesară reamintirea
câtorva cunoștințe de bază din Python.
A. S-a spus de la începutul lucrării că totul, în Python, este obiect (inclusiv clasele), iar numele
sunt simple legături către obiectele existente.
În acest context și funcțiile sunt, de asemenea, obiecte, care au propriile atribute. De un același
obiect funcție se pot lega diferite nume.
Exemplul 16.1 #Atribuirea mai multor nume la un obiect funcție
def numef1(msg):
print(msg)
numef1("Salut!")
numef2 = numef1
numef2("Salut!")
Output
Salut!
Salut!
Se observă că ambele funcții numef1 și numef2 se referă la același obiect funcție și dau în
urma execuției același rezultat.
B. Un alt aspect ce trebuie să fie reamintit este că, în Python, o funcție poate fi argumentul unei
alte funcții. O astfel de funcție care poate avea drept argument o altă funcție este denumită
funcție de ordin superior (higher order function). Exemple bine cunoscute de astfel de
funcții sunt map, filter și reduce.
Exemplul 16.2 #Utilizarea unei funcții ca argument
#Se compilează batch și se apelează în consolă.
def inc(x):
return x + 1
def dec(x):
return x - 1
def operatie(func, x):
rezultat = func(x)
return rezultat
Output
>>> operatie(inc,3)
227 Decoratoare
4
>>> operatie(dec,3)
2
C. De asemenea, în Python, o funcție poate returna o altă funcție.În continuare se prezintă cazul
unei funcții incuibate, fn_returnata(), care este definită și returnată la fiecare apel al funcției
fn_apelata().
Output
Salut!
Output
Aceasta functie este nedecorata!
Aceasta funcție este decorata!
Aceasta functie este nedecorata!
f_decorata = f_decoreaza(f_oarecare)
Funcția f_oarecare a fost decorată, iar funcției întoarse i s-a dat numele f_decorata.
Se poate observa că funcția decoratoare a adăugat ceva funcționalitate funcției originale. Ea a
acționat asemănător aplicării unui ambalaj. Obiectul care a fost decorat nu a fost afectat în nici
un fel. Dar, în urma decorării, fie are un aspect mai bun, fie are funcțiuni adăugate.
Utilizarea simbolului @
În general, decorarea unei funcții se face aplicându-i o funcție de decorare și apoi auto-
reatribuind-o, astfel:
f_oarecare = f_decoreaza(f_oarecare)
Deoarece această construcție este tipică, Python dispune de o sintaxă pentru a o simplifica, prin
utilizarea simbolului @. Simbolul @ este aplicat înaintea numelui funcției decoratoare care se
plasează chiar deasupra definiției funcției ce trebuie decorată.
Secvența care urmează:
@f_decoreaza
def f_oarecare():
print("Aceasta functie este nedecorata!")
Output
1.2
>>> impartire(6,0)
Traceback (most recent call last):
. . .
impartire(6,0)
. . .
ZeroDivisionError: division by zero
Utilizarea funcției poate fi îmbunătățită prin aplicarea unui decorator care să verifice condițiile
de corectitudine ale calculului.
229 Decoratoare
@impartire_verif
def impartire(a, b):
print(a/b)
Observație. Parametrii funcției f_interna din interiorul funcției decorator sunt aceiași cu
parametrii funcției pe care o decorează. Această constatare se poate generaliza, obținându-se o
regulă pentru decoratorii cu orice număr de parametri.
Soluția este utilizarea formei generale de definiție a unei funcții: function(*args,
**kwargs). Primul argument, args, reprezintă un tuplu de argumente poziționale, iar al doilea
reprezintă un dicționar de argumente.
Exemplul 16.7 #formă generală pentru un decorator
def deco_toate_fcn(func):
def f_interior(*args, **kwargs):
print("Decorare pentru orice funcție")
return func(*args, **kwargs)
return f_interior
return f_interior
@simbol_1
@simbol_2
def afisare(msg):
print(msg)
afisare("|| Tehnologia Informatiei ||")
Output
****************************************
========================================
|| Tehnologia Informatiei ||
========================================
****************************************
Sintaxa:
@simbol_1
@simbol_2
def afisare(msg):
print(msg)
Ordinea în care sunt înlănțuiți decoratorii are importanță; dacă se inversează ordinea în
exemplul de mai sus, se obține:
@simbol_2
@simbol_1
def afisare(msg):
print(msg)
Output
========================================
****************************************
|| Tehnologia Informatiei ||
****************************************
========================================
16.5.2 Scrieți un program care decorează o funcție calcul ce realizează operații între doi
parametri. ”Decorarea” constă în introducerea parametrilor de la consolă, lucru care nu se face
în cadrul funcției calcul.
R16.5.2 Funcția decoratoare stabilește modul cum se introduc și se transmit funcției calcul
valorile parametrilor. O singură mențiune: declararea parametrilor trebuie făcută în programul
principal.
def functia_decoratoare(func):
# def interior(*args, **kwargs): #cazul general
def interior(a, b): #cazul aplicatiei
print("Msg: urmeaza decorarea!")
a = int(input("a="))
b = int(input("b="))
print("Msg: inainte de executia funct ce se decoreaza")
# obtinerea valorii returnate
# valoarea_returnata = func(*args, **kwargs) #cazul general
valoarea_returnata = func(a, b) #cazul
aplicatiei
print("Msg: dupa executie")
a, b = 0, 0
# obtinerea rezultatului
print("Rezultatul calculului=", calcul(a, b))
Output
Msg: urmeaza decorarea!
a=6
b=9
Msg: inainte de executia funct ce se decoreaza
Msg: interiorul functiei calcul
Msg: dupa executie
Rezultatul calculului= 15
232 Decoratoare
16.5.3 Să se scrie un program în care cei doi parametri ai unei expresii de calcul sunt adăugați
consecutiv, prin două funcții decoratoare diferite.
R16.5.3 Rezolvarea constă în utilizarea metodei parametrilor înlănțuiți. Programul este
implementarea expresiei: decorator2(decorator1(calcul))).
def decorator2(func):
def intern(x, y):
x = int(input("Decorator2: x = "))
return func(x, y)
return intern
def decorator1(func):
def intern(x, y):
y = int(input("Decorator1: y = "))
return func(x, y)
return intern
@decorator2
@decorator1
def calcul(x, y):
return x * x + 10 * x + 7 + 3 * y + y * y
x = y = 0
print(calcul(x, y))
Output
Decorator2: x = 9
Decorator1: y = 5
218
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 17
După corectarea erorilor de sintaxă, încă mai pot apărea erori la momentul execuției. Acestea
sunt denumite excepții sau erori logice.
Exemple de cazuri când se produc erori:
- Încercarea de deschidere a unui fișier inexistent, pentru citire ( FileNotFoundError)
- încercarea de a divide un număr prin zero (ZeroDivisionError)
- încercarea de a importa un modul inexistent (ImportError).
234 Erori și excepții
- etc.
Ori de câte ori apare la execuție o astfel de eroare, Python creează un obiect de tip excepție.
Dacă acesta nu este procesat corespunzător, atunci programul se întrerupe și se afișează o serie
de mesaje indicând cauza și locul producerii erorii în program sau memorie, posibil și alte
detalii, după caz.
Exemplul 17.2 #cazuri de apariție a erorilor la execuția instrucțiunilor
>>> 1/0
Traceback (most recent call last):
. . . . . . . . .
1/0
ZeroDivisionError: division by zero
>>> open("inexistent.txt")
Traceback (most recent call last):
. . . . . . . . .
open("inexistent.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'inexistent.txt'
Așa cum s-a precizat mai sus, Python dispune de multe excepții de tip built-in, sau definite de
utilizator, care sunt ridicate când un program întâmpină o eroare (ceva greșit în program).
Când se ridică o excepție (se semnalizează existența sau apariția erorii), interpretorul Python
oprește execuția procesului curent și transferă controlul procesului apelat prin excepție. Dacă
tratarea excepției eșuează, atunci programul se oprește definitiv, altfel se revine la procesul
curent și execuția continuă.
De exemplu, fie o funcție A care apelează o funcție B, care apelează o funcție C. Dacă o
excepție are loc în funcția C, dar nu este procesată în C, atunci excepția trece în B și apoi în A.
Dacă nu este tratată în vreun fel, se afișează un mesaj de eroare și programul se întrerupe cu
eroare neprevăzută.
try:
print("Elementul este:", element)
r = 1/int(element)
break
except:
print("Atentie!", sys.exc_info()[0], "a avut loc.")
print("Următorul element.")
print()
print("Inversul lui:", element, "este", r)
Output
Elementul este: s
Atentie! <class 'ValueError'> a avut loc.
Următorul element.
Elementul este: 0
Atentie! <class 'ZeroDivisionError'> a avut loc.
Următorul element.
Elementul este: 7
Inversul lui: 7 este 0.14285714285714285
Această secvență de program conține o buclă for pentru elementele din lista xlista. Sursa
posibilei excepții se află în interiorul blocului try.
Dacă nu se produce nici o excepție, blocul except este sărit (ultimul caz), iar fluxul normal
continuă până la ultima valoare. Dar dacă se produce o excepție (primul și al doilea caz),
aceasta este prinsă de blocul except.
Se poate afișa numele excepției prin funcția exc_info() din modulul sys. Se poate observa
că elementul s produce ValueError, iar 0 produce ZeroDivisionError.
Nu este o practică bună de programare să se prindă toate excepțiile și să se trateze fiecare caz
în același mod.
Se poate specifica ce excepții ar trebui să prindă o clauză except.
O clauză try poate să aibă orice număr de clauze except pentru a trata diferite excepții, totuși
doar numai una va fi executată în cazul ridicării unei excepții.
Se poate utiliza un tuplu de valori pentru a specifica mai multe excepții într-o singură clauză
except.
try:
# se face ceva
pass
except ValueError:
# se tratează excepția ValueError
pass
237 Erori și excepții
except:
# tratează alte excepții
pass
>>> try:
... a = int(input("Introduceti un pozitiv intreg: "))
... if a <= 0:
... raise ValueError("Acesta nu este un numar pozitiv!")
... except ValueError as val_eroare:
... print(val_eroare)
...
Introduceti un pozitiv intreg: -2
Acesta nu este un numar pozitiv!
Prin acest tip de construcție, dacă apare o eroare la deschiderea fișierului sau la desfășurarea
operațiilor cu acesta, producând o excepție la execuția programului, se închide fișierul și se
eliberează resursele implicate în deschiderea acestuia.
A crea o excepție în Python înseamnă a crea o clasă nouă. Clasa excepție definită de utilizator
trebuie să fie derivată direct sau indirect din clasa built-in Exception. Majoritatea excepțiilor
built-in sunt de asemenea derivate din aceasta.
Mai sus s-a creat excepția definită de utilizator denumită Test_eroare care moștenește clasa
Exception. Această excepție nouă, ca și alte excepții, poate fi ridicată folosind instrucțiunea
raise cu un mesaj opțional de eroare.
O clasă definită de utilizator poate fi implementată ca orice altă clasă făcând orice face o clasă
normală. În general, totuși, această clasă trebuie să fie simplă și concisă. Frecvent, în multe
aplicații, se declară o clasă excepție de bază utilizator, iar alte excepții sunt derivate din această
clasă.
Exemplul 17.9 Excepție definită de utilizator
# Se cere introducerea unui numar pana ce acesta este dat corect. Se oferă
#o indicatie ajutatoare precizând daca numarul este mai mare sau mai mic
#decat cel asteptat, corect.
class Eroare(Exception):
"""Clasa de baza pentru alte exceptii"""
pass
class Eroare_Val_Mica(Eroare):
"""Exceptie ridicata cand valoarea introdusa este mai mica"""
pass
class Eroare_Val_Mare(Eroare):
""" Exceptie ridicata cand valoarea introdusa este mai mare"""
pass
# Numarul de ghicit este:
numar = 10
# se fac incercari pana se ghiceste
while True:
try:
240 Erori și excepții
In exemplul de mai sus s-a definit clasa de baza Eroare. Celelalte două excepții care sunt
ridicate în secvența de program: Eroare_Val_Mica , Eroare_Val_Mare, sunt derivate din
această clasă.
Observație. Mai sunt și alte metode de a defini clase utilizator.
17.4.2 Excepția AssertionError
Un caz special în rularea unui program este cel în care se așteaptă îndeplinirea unei condiții
date (assert) pentru a continua rularea, neîndeplinirea acesteia oprind programul. Altfel spus,
dacă condiția este True, programul continuă, iar dacă este False, se ridică o excepție
AssertionError.
Exemplul 17.11
class SalariulNuEsteInInterval(Exception):
"""Excepție ridicată pentru erori la introducerea valorii salariului.
Semnificatia parametrilor:
salariul – salariul introdus (poate genera eroare)
mesaj – explicatia erorii
"""
def __init__(self, salariul, mesaj="Salariul nu este in intervalul
(5000, 15000)"):
self.salariul = salariul
241 Erori și excepții
self.mesaj = mesaj
super().__init__(self.mesaj)
salariul = int(input("Introduceti valoarea salariului:"))
if not 5000 < salariul< 15000:
raise SalariulNuEsteInInterval(salariul)
Output
Introduceti valoarea salariului: 2000
Traceback (most recent call last):
. . .
raise SalariulNuEsteInInterval(salariul)
SalariulNuEsteInInterval: Salariul nu este in intervalul (5000, 15000)
În exemplul de mai sus, s-a suprascris funcția de inițializare a clasei Exception pentru a
accepta argumentele utilizator salariul și mesaj. Apoi, constructorul clasei părinte
Exception este apelat manual cu argumentul self.mesaj utilizând super().
În exemplul care urmează, metoda moștenită __str__ a clasei Exception este utilizată pentru
a afișa mesajul corespunzător când excepția SalariulNuEsteInInterval este ridicată. Pentru
adaptare, metoda __str__ este suprascrisă.
Exemplul 17.12
class SalariulNuEsteInInterval(Exception):
"""Excepție ridicată pentru erori la introducerea valorii salariului.
Semnificatie:
salariul – salariul introdus (poate genera eroare)
mesaj – explicatia erorii
"""
def __init__(self, salariul, mesaj="Salariul nu este in intervalul
(5000, 15000)"):
self.salariul = salariul
self.mesaj = mesaj
super().__init__(self.mesaj)
def __str__(self):
return f'{self.salariul} -> {self.mesaj}'
Output
Introduceti valoarea salariului: 2000
Traceback (most recent call last):
. . .
raise SalariulNuEsteInInterval(salariul)
SalariulNuEsteInInterval: 2000 -> Salariul nu este in intervalul (5000,
15000)
- OutOfMemoryError,
- StackOverflowError, etc.
Excepția este un eveniment care poate produce oprirea programului, dar pe care programatorul
îl poate testa și îl poate ocoli, astfel încât să se evite oprirea programului. Excepția poate fi
controlată și anticipată. Exemple de excepții:
- FileNotFoundException,
- ParseException,
- ZeroDivisionError, etc.
17.6.2 O instrucțiune try poate avea mai multe instrucțiuni except?
R17.6.2 Da, acest lucru este folosit când blocul try conține instrucțiuni care pot genera mai
multe tipuri de excepții.
17.6.3 Se poate scrie o clauză except generică, ce poate trata orice excepție?
R17.6.3 Da. In clauza except nu se specifică nimic, se va activa pentru oricare excepție
determinată de blocul din clauza try.
17.6.4 După o clauză except se poate include o clauză else. Această clauza else se poate
combina cu if sau cu elif?
R17.6.4 Codul din blocul else se execută dacă blocul try nu ridică nici o excepție. În acest
bloc se poate plasa partea de cod care nu are nevoie de protejarea prin clauza try. Nu este
posibil în nici un fel combinarea acestui else cu if sau elif. Singura posibilitate este
adăugarea instrucțiunii if după clauza else, după modelul următor:
try:
. . .
except
. . .
else:
. . .
if …:
. . .
elif . . .:
. . .
else:
. . .
17.6.5 Se poate scrie o clauză except pentru mai multe excepții (excepții multiple) ?
R17.6.5 Da, după cum se poate constata din modelul următor:
try
. . .
Operatii
. . .
except(Exceptie1 [, Exceptie2 [, . . . ExceptieN]]])
else
Bloc executabil daca nu exista nici o exceptie
args – una sau mai multe valori. Dacă lipsește (este opțional), valoarea sa este None.
traceback – un obiect traceback (rar folosit în practică)
17.6.7 Să se arate că orice excepție moștenește clasa de bază Exception. Se poate utiliza codul
de la exercițiul 17.3 (cu utilizarea clauzelor try și except, cu moștenirea clasei de bază)
R17.6.7
import sys #modulul sys conține tipul de excepție
xlista = ['s', 0, 7]
for element in xlista:
try:
print("Elementul este:", element)
r = 1/int(element)
break
except:
print("Atentie!", element.__class__, Exception.__init__, "a avut
loc.")
print("Următorul element.")
print()
print("Inversul lui:", element, "este", r)
Output
Elementul este: s
Atentie! <class 'str'> <slot wrapper '__init__' of 'Exception' objects> a
avut loc.
Următorul element.
Elementul este: 0
Atentie! <class 'int'> <slot wrapper '__init__' of 'Exception' objects> a
avut loc.
Următorul element.
Elementul este: 7
Inversul lui: 7 este 0.14285714285714285
17.6.8 Arătați cum se poate asigura că data introdusă este un număr, folosind o buclă while.
R17.6.8
while True:
try:
numar = int(input('Introduceti un numar:'))
break
except ValueError:
print('Acesta nu este un numar, mai incercati')
Output
Introduceti un numar:f
Acesta nu este un numar, mai incercati
Introduceti un numar:%
Acesta nu este un numar, mai incercati
Introduceti un numar:5
17.6.9 Fie dat un dicționar unde cheia este un număr întreg, iar valoarea o sumă de bani (de
exemplu: d = [{6:350}, {23:2345}, {35:890}, {52:1234}). Să se scrie un program care să
parcurgă dicționarul pe baza valorilor întregi dintr-un interval și să se totalizeze suma de bani.
Dacă în dicționar nu există cheia corespunzătoare unui index, se va continua generând o
excepție.
244 Erori și excepții
R17.6.9
d = {2:350, 3:2345, 35:890, 52:1234}
tot = 0
for i in range(len(d)):
print(i)
try:
tot += d[i]
except KeyError:
print('Nu exista cheia =', i)
i += 1
print('Total: ', tot)
Output
0
Nu exista cheia = 0
1
Nu exista cheia = 1
2
3
Total: 2695
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 18
Șirul de 5 caractere de mai sus definește un șablon RegEx. Șablonul are semnificația: oricare
șir de cinci caractere alfanumerice începând cu m și terminând cu r. Modul de funcționare
este prezentat în tabelul de mai jos.
Expresie ^m...r$
Șir mortar motor mosor Motor montor
Potrivire nu da da nu nu
import re
pattern = '^a...t$' #sablonul are 5 caractere (3 interioare)
sir_testat = 'avizat' #sirul are 6 caractere (4 interioare)
resultat = re.match(pattern, sir_testat)
if resultat:
print("Cautare reusita")
else:
print("Cautare nereusită")
Output
Cautare nereusită
Exemplul 18.3 #Căutare șir după șablon șir încadrat, cu 4 caractere interioare
import re
pattern = '^a....t$'
sir_testat = 'avizat'
resultat = re.match(pattern, sir_testat)
if resultat:
print("Cautare reusita")
else:
print("Cautare nereusită")
Output
Cautare reusita
246 Expresii regulate
În secvențele de program de mai sus s-a utilizat funcția re.match() pentru a căuta după un
șablon/formă/pattern în șirul test_string. Metoda întoarce obiectul “potrivit”/recunoscut, în caz
de succes al căutării. Altfel, întoarce None.
În modulul re sunt definite mai multe funcții pentru a lucra cu RegEx. Pentru a le utiliza, sunt
necesare câteva cunoștințe despre expresii regulate.
Expresie [abc]
Șir a ac televizor minerala
Potrivire da (1) da (2) nu da (2)
Expresie ..
Șir a ac televizor programare
Potrivire nu (0) da (1) da (4) da (5)
^ - Caret. Simbolul caret ^ se utilizează pentru a verifica dacă un șir începe cu un anumit
caracter.
Expresie ^a
Șir a abc bac
Potrivire Da da nu
Expresie ^al
Șir a abc altul
Potrivire nu nu da
$ - Dollar. Simbolul $ este utilizat pentru a verifica dacă un șir se termină cu un anumit
caracter.
Expresie a$
Șir a apa car
Potrivire da da nu
* - Asterix (star). Simbolul * verifică zero sau mai multe apariții care se potrivesc cu el.
Expresie ma*t
Șir material mtt marcat maat programat
Potrivire da da nu da da
+ - Plus. Simbolul + verifică una sau mai multe apariții care se potrivesc cu el.
Expresie ma+t
Șir material mtt marcat maat programat
Potrivire da nu nu da da
? – Semnul întrebării (question mark). Simbolul ? verifică zero sau cel mult una apariții
care se potrivesc cu el.
Expresie ma?t
Șir material mtt marcat maat programat
Potrivire da da nu nu da
248 Expresii regulate
Expresie a{}
Șir material mtt marcat maaaat
Potrivire da nu da nu
Expresie [0-9]{2,3}
Șir pt15xyz c2345x 6 sau 7 77 și 888
Potrivire da nu nu da
Expresie c|t
Șir material mtt marcat mare cip
Potrivire da (1) da (2) da (2) nu Da (1)
Șablonul c|t găsește potrivire în șirurile care conțin fie c, fie t, sau ambele.
\ - Backslash-ul \ este utilizat pentru a obține diverse caractere inclusiv toate metacaracterele.
De exemplu, \$a se potrivește dacă un șir conține $ urmat de a. Aici, $ nu este interpretat de
RegEx într-un mod special. Dacă există o nesiguranță în ce privește semnificația specială a
unui caracter, se poate pune caracterul \ în fața acestuia. Ca urmare, caracterul nu este tratat în
vreun mod special.
Secvențe speciale. Permit scrierea mai ușoară a schemelor frecvente. În continuare se prezintă
câteva secvențe speciale mai des întâlnite în practică.
Expresie \Aale
Șir alegator alfabet alergător al
nostru
Potrivire da nu da nu
\B – Opusul lui \b. Potrivire, caracterele specificate nu se află la începutul sau la sfârșitul
cuvântului.
249 Expresii regulate
Expresie \d
Șir 12abc3 abbcd “z32A%9Sq Windows 11
Potrivire da(3) nu da(3) da(2)
Expresie \D
Șir 1ab34”50 2021 Python 3.10 8th
Potrivire da(3) nu da(7) da(2)
\s– Potrivire pentru cazul când șirul conține orice caracter spațiu alb (whitespace).
Echivalent cu [ \t\n\r\f\v].
Expresie \s
Șir Python RegEx PythonRegEx Python
RegEx
Potrivire da (1) nu da(1)
Expresie \S
Șir ab Windows 11 “ “
3spații
Potrivire da(2) da(9) nu
\w– Potrivire, orice caracter alfanumeric (cifră sau literă) Echivalent cu [a-zA-Z0-9_].
Observație: underscore ( _ ) este considerat caracter alfanumeric.
Expresie \w
Șir 12&” ;c %”#: s-au testat d’ale carnavalului
Potrivire da (3) nu da(10) da(17)
Expresie \W
Șir 1a2&c abcde v-au răspuns La multi ani!
Potrivire da(1) nu da(1) da(1)
>>>print(re.findall("\W", sir))
[' ', ' ', ' ', '$', '$', '$', ' ', ' ']
>>>print(re.findall("\W+", sir))
[' ', ' ', ' $$$ ', ' ']
Expresie cd\Z
Șir a fxc ecd a fxc ecd x Sa 65cd zszs
Potrivire da nu nu
Modulul conține mai multe funcții și constante pentru lucrul cu expresii regulate.
1) re.match(). Funcția match() va căuta modelul expresiei regulate sablon în sir
(începând cu primul caracter, doar în primele caractere) și va returna True dacă un obiect este
găsit (la prima apariție) , alfel va returna False (se iau în considerare indicatorii).
Sintaxa:
re.match(sablon, sir, indicatori = 0)
3) re.split(). Metoda divizează un șir inițial în care există o potrivire și întoarce o listă a
șirurilor în care s-a făcut divizarea șirului inițial (sablonul reprezintă separatorii dintre șiruri).
Sintaxa:
re.split(sablon, sir)
4) re.sub(). Metoda întoarce un șir în care potrivirile găsite sunt înlocuite cu conținutul
variabilei sir_substituire.
Sintaxa:
rezultat = re.sub(sablon, sir_substituire, sir)
6) re.search(). Metoda caută în tot șirul și va da numai prima locație unde șablonul are o
potrivire cu șirul. Dacă are succes, re.search() întoarce obiectul potrivit, altfel întoarce None.
Sintaxa:
rezultat = re.search(sablon, sir)
Metode:
- group(). Metoda întoarce acea parte din șir care s-a potrivit căutării.
- start(). Metoda întoarce indexul de start al subșirului găsit prin potrivire.
- end(). Metoda întoarce indexul de sfârșit al subșirului găsit prin potrivire.
- span(). Metoda întoarce un tuplu care conține indecșii de început și sfârșit ai potrivirii
găsite.
Atribute:
- re. Atributul întoarce obiectul expresie regulată care s-a utilizat la căutarea prin potrivire..
- string. Atributul întoarce șirul în care s-a căutat.
Output
['\n', '\r']
Output
Acesta este sirul 12 de test
None
tAcesta este sirul 12 de test
<re.Match object; span=(0, 2), match='tA'>
Output
['56', '98', '123']
Output
['ab$', 'mk', '$$$nbg', 'ASDRS']
Observație. Dacă șablonul nu este găsit, re.split() întoarce o listă conținând șirul original.
De asemenea, se poate utiliza argumentul maxsplit pentru metoda re.split(), prin care se
precizează numărul maxim de divizări permis.
Exemplul 18.8 #Diviziune cu număr maxim de diviziuni cu re.split()
import re
sir = 'ab$56mk98$$$nbg123ASDRS'
sablon = '\d+'
#nr. max. de divizari (maxsplit) = 1
sirulete = re.split(sablon, sir, 1)
print(sirulete)
Output
['ab$', 'mk98$$$nbg123ASDRS']
Output
iuhbYTF986hbjbGGHHWEW87SAA\fgEE075ez
253 Expresii regulate
Observație. start() a găsit indexul de început al potrivirii în șir, end() a găsit indexul de
sfârșit al potrivirii în șir, iar span() a dat tuplul conținând cei doi indecși.
Exemplul 18.14 # Întoarcerea expresiei regulate și a șirului utilizați în căutare
>>> raspuns.re
re.compile('(\\d{3}) (\\d{2})')
>>> raspuns.string
'39801 356, 2102 1111'
18.6.2 Să se scrie un program pentru găsirea unui șir format dintr-un caracter “x” și un număr
de 0 sau 1 caractere “y”.
R18.6.2
import re
def potrivire(sir):
schema = 'ab?'
if re.search(schema, sir):
return 'Potrivire gasita'
else:
return 'Nu s-a gasit potrivirea'
print(potrivire("a"))
print(potrivire("ab"))
print(potrivire("abc"))
print(potrivire("xabbc"))
print(potrivire("yaabbc"))
Output
Potrivire gasita
Potrivire gasita
Potrivire gasita
Potrivire gasita
Potrivire gasita
18.6.4 Care este sablonul de potrivire a unui subșir compus dintr-un “a” urmat de doi sau trei
“b” într-un alt sir?
R18.6.4 Șablon = ‘ab{2,3}’. Se utilizează cu funcția re.search().
255 Expresii regulate
18.6.5 Care este șablonul de potrivire a unui subșir, conținând un “r”, într-un alt sir?
Exemplificați.
R18.6.5
import re
def potrivire(sir):
schema = ('\w*r.\w*')
if re.findall(schema, sir):
print(re.findall(schema, sir))
return 'Potrivire gasita'
else:
return 'Nu s-a gasit potrivirea'
sir = "greutatea se măsoară în kilograme"
print(potrivire(sir))
Output
['greutatea', 'măsoară', 'kilograme']
Potrivire gasita
18.6.6 Care este șablonul de potrivire a unui subșir, conținând litere mici, litere mari, cifre și
underscore, într-un alt sir?
R18.6.6 Sablonul este sirul '[a-zA-Z0-9_]*'. Exemplu:
>>>import re
>>>sir = "%Ziua de nastere este @25@ Noiembrie 2010"
>>>sir
'%Ziua de nastere este @25@ Noiembrie 2010'
>>>schema = '[a-zA-Z0-9_]*'
>>>print(re.findall(schema, sir))
['', 'Ziua', '', 'de', '', 'nastere', '', 'este', '', '', '', '25', '', '',
'Noiembrie', '', '2010', '']
Capitolul 19
NumPy este o bibliotecă sub forma unui pachet Python. Numele reprezintă o abreviere de la
Numerical Python. Biblioteca este formată din obiecte de tip matrice multidimensionale și o
colecție de rutine pentru procesarea acestor obiecte. Numeric, strămoșul NumPy, a fost
dezvoltat de Jim Hugunin. De asemenea, a fost dezvoltat un alt pachet Numarray, având câteva
funcționalități suplimentare. În 2005, Travis Oliphant a creat pachetul NumPy prin
încorporarea caracteristicilor Numarray în pachetul Numeric.
Cu NumPy se pot face următoarele:
- Operații matematice și logice pe tablouri.
- Transformate Fourier și rutine pentru manipularea formelor.
- Operații legate de algebra liniară.
- NumPy are funcții încorporate pentru algebră liniară și generarea de numere aleatoare.
NumPy poate fi considerat un înlocuitor pentru Matlab, dacă este utilizat împreună cu pachete
precum SciPy (Scientific Python) și Matplotlib (bibliotecă de funcții grafice). Succesul pe scară
largă ca înlocuitor pentru MATLAB, cea mai cunoscută platformă pentru calculul tehnic, se
datorează în principal faptului că este open-source.
Practic, alternativa Python și a ecosistemului său la MATLAB este văzută în prezent ca
reprezentând mult mai mult: un mediu de programare mai modern și mai complet.
Ipython este un shell interactiv (consolă) Python care are multe caracteristici interesante: acces
la comenzi shell, depanare (debugging), etc.
Pylab este o interfață care permite executarea unor sesiuni interactive, asemănătoare cu
MATLAB din punct de vedere al funcționalității. Pylab este o interfață pentru biblioteca de
funcții de reprezentare grafică, orientată pe obiecte, denumită matplotlib.
Matplotlib este o bibliotecă grafică pentru generarea reprezentărilor grafice științifice 2D și
3D. Dispune de o serie de facilități care permit obținerea de figuri de bună calitate, în multe
formate (png, pdf, svg, eps, etc.) necesare publicațiilor științifice.
Deorece a fost modelată foarte apropiat de MATLAB, majoritatea comenzilor de afișare
grafică, inclusiv parametrii acestora, sunt similare cu ale MATLAB-ului.
Seaborn este o bibliotecă pentru reprezentări statistice ale datelor în Python. Ea este bazată pe
Matplotlib și se află în strânsă legătură cu structurile de date utilizate de Pandas. Scopul
Seaborn este realizarea de reprezentări grafice aspectuoase ale datelor, dar în același timp să
ușureze și înțelegerea acestora și să scoată în evidență detaliile mai importante.
Funcțiile de reprezentare grafică operează asupra tablourilor de date și structurilor dataframe
din Pandas, efectuând toate prelucrările necesare asupra seturilor de date (“dataset oriented”)
257 Ecosistemul Python, prezentare generală
astfel încât din grafice să rezulte maximum de informație. Utilizatorul se poate concentra astfel
mai mult asupra semnificațiilor datelor și mai puțin pe aspectele de programare a graficelor.
19.1.4 Pandas
Pandas este o bibliotecă de o utilitate deosebită destinată analizei datelor (“Python Data
Analysis Library ”), numele său fiind posibil o prescurtare aproximativă pentru “panel data”.
Pandas a fost creată de Wes McKinney in 2008.
Cu și prin Pandas, procesarea datelor poate fi realizată în cinci pași: Încărcare, Pregătire,
Manipulare, Modelare, Analiză.
Pandas este construită pe baza NumPy și SciPy și poate fi văzută ca o simplă API care creează
funcționalitate mai bună pentru lucrul cu date eterogene organizate sub formă de serii de timp
(Series, 1 dimensiune), tablouri de date (DataFrame, 2 dimensiuni), containere de tablouri de
date (Panel, 3 dimensiuni). În consecință, deși nu este revoluționară, Pandas aduce multe
avantaje utilizatorilor.
Utilitatea Pandas apare atunci când se prelucrează date stocate în fișiere și baze de date care
conțin atât numere cât și șiruri, cum ar fi SQL și Excel. În cazul interogării (într-un anumit sens
asemănător modului în care se face în SQL) a unei baze de date în Pandas, se poate fece o citire
directă cu afișare printr-o singură linie de comandă.
În comparație cu NumPy care este proiectat pentru utilizarea general-eficientă a memoriei,
Pandas obține o performanță mai bună pentru tablouri care au un număr de rânduri mai mic de
500000. Indexarea seriilor Pandas este mai lentă decât indexarea tablourilor NumPy.
Instalarea Pandas se poate face cu utilitarul pip, sau automat prin instalarea Anaconda.
Resurse: https://pandas.pydata.org/
TensorFlow este o bibliotecă open-source pentru învățare automată (Machine Learning, ML)
și învățare profundă (Deep Learning, DL).
TensorFlow fost produs de echipa Google Brain și a fost făcut public prima dată în noiembrie
2015.
TensorFlow combină algebra de calcul și tehnicile de optimizare pentru calcularea ușoară a
multor expresii matematice. Caracteristicile cele mai importante ale TensorFlow sunt:
- definește, optimizează și calculează cu ușurință expresiile matematice cu ajutorul unor
tablouri multidimensionale numite tensori.
- include un suport de programare pentru rețele neuronale profunde (Deep Neural Network,
DNN) și tehnici de învățare automată (ML).
- TensorFlow folosește unitatea de calcul GPU, automatizând managementul acesteia. De
asemenea, include metode de optimizare a datelor utilizate și a funcționării GPU.
Scikit-Learn este o bibliotecă de algoritmi din domeniul Machine-Learning pentru rezolvarea
de probleme de tip clasificare și învățare supervizată și nesupervizată. Este ușor de folosit.
Caracteristicile sale sunt:
- Unealtă simplă și eficientă pentru analiza datelor.
- Ușor utilizabilă în contexte diverse.
- Construită numai pe baza NumPy, SciPy și Matplotlib.
- Open-source, licență BSD utilizabilă comercial.
- Are aplicații în: clasificare, regresie, clustering, reducerea dimensionalității datelor,
preprocesare, modelare, etc.
Resurse: http://scikit-learn.sourceforge.net și https://scikit-learn.org/
Keras este o bibliotecă open-source în Python pentru rețele neuronale artificiale (DNN).
Acționează ca o interfață pentru biblioteca TensorFlow 2.0.
A fost dezvoltat ca parte a unui proiect de cercetare și are ca prim autor și susținător un inginer
de la Google, François Chollet.
Keras conține numeroase blocuri preconstruite, componente pentru rețelele neuronale, ca:
layere, funcții de activare și optimizare, suport și unelte de lucru cu date de tip imagine și text
necesare pentru modelele de rețele neuronale tip convoluțional și recurent. Permite antrenarea
modelelor pe dispozitive GPU (Graphics Processing Unit) și TPU (Tensor Processing Unit).
Theano este o bibliotecă pentru Deep Learning (pentru antrenarea modelelor DNN),
dezvoltată de Universitatea din Montreal în 2007.
Theano este construit pe baza NumPy, având o interfață similară cu NumPy, fiind o bibliotecă
de funcții care permite definirea, optimizarea și evaluarea eficientă a expresiile matematice
care implică tablouri multi-dimensionale.
Mulți cercetători din domeniul Deep Learning s-au bazat pe Theano în dezvoltarea de modele
Deep Learning.
Autorul Theano, Yoshua Bengio, a anunțat in 2017 că dezvoltarea Theano încetează.
Pytorch este o bibliotecă open-source pentru Machine Learning bazată pe biblioteca Torch,
utilizată anterior pentru aplicații de vederea mașinilor și procesarea limbajului natural,
dezvoltată de Facebook în laboratorul AIResearch (FAIR).
259 Ecosistemul Python, prezentare generală
Bottle. WSGI (Web Server Gateway Interface) este o specificație care descrie cum comunică
un server web cu aplicațiile web și cum aplicațiile web pot fi înlănțuite pentru a procesa o
cerere. Bottle este un micro-cadru de lucru de tip WSGI pentru Python. Bottle este distribuit
ca un singur fișier modul și nu are alte dependențe în afară de Python.
PyScript este un cadru de lucru recent dezvoltat și în curs de testare care permite crearea de
aplicații Python într-un browser. Dezvoltatorii PyScript încă nu recomandă utilizarea acestuia
în “production”, deoarece este posibil să aibă unele deficiențe și să urmeze numeroase
modificări.
Alte cadre de lucru pentru Web Development mai sunt și Tornado, CherryPy, Web2py, etc.
Trac este un sistem open-source, de dezvoltare a proiectelor software pentru web, de urmărire
a bug-urilor, controlul versiunii și pagini wiki.
Salt (SaltStack) este un software open-source pentru automatizarea unor procese IT pe baza
evenimentelor, execuția la distanță a taskurilor și administrarea configurațiilor. Este utilizat de
Linkedin, NASA, etc.
Open Stack SDK Python este unul din serviciile Open Stack. Este destinat realizării unui SDK
(Software Development Toolkit), într-o interfață unitară, în ajutorul clienților bibliotecilor și
interfețelor Python.
PyGlet este un software open-source, o bibliotecă pentru dezvoltarea jocurilor și altor aplicații
cu interfață grafică complexă pe cele mai cunoscute platforme (Windows, Mac OS X și Linux).
PyOpenGL este cel mai cunoscut software open-source multiplatformă care leagă Python de
OpenGL. Este interoperabil cu un număr de biblioteci GUI: wxPython, PyGame, PyQt,
PySyde, PyGTK, Tkinter.
261 Ecosistemul Python, prezentare generală
Arcade este o bibliotecă Python pentru realizarea jocurilor cu grafică și sunete. La fel ca și
PyGame, este destinat jocurilor 2D, dar spre deosebire de PyGame care este orientat spre
grafica rastru, Arcade utilizează OpenGL (grafică vectorială) și are susținere pentru utilizarea
GPU pentru accelerarea randării, fiind foarte rapid.
Panda3D este un cadru de lucru open-source pentru randare 3D și jocuri real-time 3D,
simulări, vizualizări, experimente. Poate fi utilizată fie din Python, fie din C/C++.
PyUnit este un cadru de test standard pentru Python permițând automatizarea testării,
distribuirea codului pentru teste, agregarea testelor în colecții, independența testelor, etc.
PyTest este un cadru de lucru care permite utilizarea secvențelor de test în Python, simple sau
complexe, pentru baze de date, API, UI, etc.
Requests este o aplicație pentru simplificarea emiterii cererilor de tip HTTP/1.1. Scopul cererii
efectuate de client este de a accesa o resursă pe un server utilizând URL.
Beautiful Soap este un software utilizat pentru extragerea datelor din pagini scrise într-un
limbaj de marcare (HTML, XML, etc.).
Selenium este un software open-source care permite testarea automată a aplicațiilor web în
raport cu browserele. Testarea manuală a unei aplicații web pentru a vedea comportarea față
262 Ecosistemul Python, prezentare generală
de un număr imens de browsere înseamnă un număr uriaș de ore de muncă pentru echipele de
testare. Selenium este destinat să evite acest efort.
lxml este o bibliotecă Python care permite prelucrarea ușoară a fișierelor XML și HTML și
care mai poate fi utilizată de asemenea pentru web scrapping. Se utilizează cu un API simplu
și puternic din Python pentru tratarea fișierelor XML și HTML.
Scrapy este un software open-source pentru extragerea informațiilor din site-ul web. Este
primul software de acest gen scris în Python. Inițial (2008) a fost creat pentru web crowling,
dar apoi a fost utilizat și pentru extragerea datelor.
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 20
Python este un limbaj de programare interactiv și în consecință atașarea unui cadru de lucru
(framework) de programare grafică (Graphical User Interface, GUI) este o chestiune naturală
și relativ simplu de realizat.
Python dispune în prezent de o gamă variată de opțiuni de interfețe de lucru de tip grafic (uzual,
în română, “interfețe GUI”). Unele dintre acestea sunt multiplatformă, sau multibrowser, altele
sunt specifice. Exemple: tkinter, PyQT, Kivy, wxPython, PyGTK, PyKDE, etc. O listă
completă a interfețelor de tip GUI se află la adresa:
https://wiki.python.org/moin/GuiProgramming
Cele mai cunoscute interfețe de tip GUI sunt prezentate pe scurt în continuare.
20.1.2 Tkinter
Tkinter este cadrul GUI standard al limbajului Python, care în mod obișnuit se instalează la
pachet cu Python. Denumirea tkinter provine de la tk interface.
Observație. Tk este o bibliotecă de elemente grafice de bază GUI dezvoltată de John
Ousterhout ca o extensie la limbajul de scripting Tcl, în 1991. Este open-source, cross-platform
(Linux, Unix, Mac OS, Windows).
Tkinter este cunoscut pentru simplitatea interfaței grafice a utilizatorului. Este open source și
este disponibil sub licența Python. Unul dintre avantajele alegerii Tkinter este că, din moment
ce vine implicit, se dispune de o abundență de resurse, atât coduri, cât și cărți de referință.
Odată scrisă o interfață în tkinter într-un sistem de operare, aceasta va funcționa și pe celelalte
sisteme de operare (este multiplatformă), dar va arăta pe fiecare în mod specific. De asemenea,
comunitatea fiind veche și activă, există multă documentație pe internet și mulți utilizatori care
pot ajuta în cazul apariției unor dificultăți.
Mai multe resurse la:
https://docs.python.org/2/library/tkinter.html.
Qt (pronunțat "cute") este un set de unelte widget pentru crearea de interfețe grafice, disponibil
ca aplicație multiplatformă (Linux, Windows, MacOS, Android sau sisteme embeded). Are un
grad mare de portabilitate, având nevoie de puține modificări ale codului de bază, la transferul
de pe o platformă pe alta.
Qt este susținut în prezent de “The Qt Company“, o firmă privată, de un proiect public open
source “Qt Project“ și de un număr mare de alte organizații și dezvoltatori individuali. Este
264 Interfețe grafice utilizator (GUI)
disponibil atât sub licență comercială, cât și sub licență open-source GPL 2.0, GPL 3.0 și LGPL
3.0.
PyQT este una dintre dezvoltările (denumite binding/“legătură”) de tip bibliotecă grafică multi
-platformă pentru Python care implementează biblioteca Qt. Aceasta face o combinație bună
între Python și Qt și permite programatorului să aleagă între programarea directă sau folosirea
Qt Designer pentru a crea dialoguri vizuale. Este disponibil atât în licență comercială, cât și în
licență GPL, care precizează că (deși este posibil ca unele funcții să nu fie disponibile în
versiunea gratuită), dacă aplicația proiectată este open source, atunci se poate folosi totuși sub
licența gratuită. PyQT a fost dezvoltat sub formă de versiuni până la PyQT5.
PySide este un set gratuit și multiplataformă de instrumente GUI Qt, inițiat și sponsorizat de
Nokia. PySide acceptă Linux/X11, Mac OS X și Windows. PySide oferă instrumente pentru
funcționarea cu documente multimedia, XML, rețea, baze de date și GUI. PySide este
compatibil API cu PyQt4.
Qt Designer este o unealtă care permite obținerea unei interfețe GUI de tip “what-you-see-is-
what-you-get (WYSIWYG)” pentru aplicații PyQt, în mod eficient. Obiectele Qwidget pot fi
trase și plasate cu mouse-ul într-un cadru-formă, gol. Apoi acestea pot fi aranjate coerent într-
un GUI utilizând manageri de straturi. Qt Designer permite previzualizarea GUI utlizând
diferite stiluri și rezoluții, conectarea semnalelor și sloturilor, crearea meniurilor și toolbar-
urilor, etc.
Qt designer nu produce cod Python, ci fișiere .ui, de tip XML, independente de platformă.
Acestea pot fi transformate în cod Python utilizând utilitarul pyuic5. Codul obținut va sta la
baza aplicației GUI.
https://wiki.python.org/moin/PyQt, https://realpython.com/python-pyqt-gui-calculator/
20.1.4. Kivy
Kivy este un GUI accelerat OpenGL ES 2 pentru crearea de noi interfețe utilizator. Suportă
mai multe platforme și anume Windows, MacOSX, Linux, Android, iOS și Raspberry Pi. Este
open source și vine cu numeroase (> 20) widget-uri în setul de instrumente.
Este preferat pentru aplicațiile pe dispozitive mobile.
Mai multe resurse la: https://kivy.org
20.1.5 WxPython
WxPython este un wrapper (învelitoare, ambalaj) Python, open source, pentru wxWidgets
(care este scris în C ++), un popular kit de instrumente GUI pe mai multe platforme, dezvoltat
de Robin Dunn împreună cu Harri Pasanen. WxPython poate fi utilizat pe Windows, Mac OS
și Unix, fiind implementat ca un modul de extensie. Modulele principale din API wxPython
includ un modul de bază conținând clasa wxObject, care este baza pentru toate clasele din API.
Modulul de control conține toate widgeturile utilizate în dezvoltarea aplicației GUI. De
exemplu, wx.Button, wx.StaticText (analog unei etichete), wx.TextCtrl (control text
modificabil) etc. De asemenea, wxPython API cuprinde modulul GDI (Graphics Device
Interface), un set de clase utilizate pentru desenarea pe widgeturi. Clase precum fontul,
culoarea, pensula etc. fac parte din acesta. Toate clasele de ferestre de containere sunt definite
în modulul Windows.
Mai multe resurse la: http://wxpython.org, https://zetcode.com/wxpython/,
265 Interfețe grafice utilizator (GUI)
https://tutorialspoint.com/wxpython/index.htm
20.1.6 PyGUI
PyGUI este un cadru multiplatformă de aplicații grafice pentru Unix, Macintosh și Windows.
În comparație cu alte cadre de lucru de tip GUI, PyGUI este de departe cel mai simplu și ușor
dintre toate, deoarece API-ul este pur sincronizat cu Python, fiind GUI-ul natural al platformei.
Mai multe resurse la: https://www.cosc.canterbury.ac.nz/greg.ewing/python_gui/
20.1.7 PyGTK
PyGTK este un set de învelișuri/ambalaje pentru biblioteca GUI de tip GTK. PyGTK este un
software de tip free, licențiat LGPL. El este analog interfețelor PyQT (sau PySide) și wxPython
care la rândul lor sunt învelișuri pentru Qt, respectiv wxWidgets. A fost dezvoltat de autorul
interfeței originale GNOME (Linux), James Henstridge.
20.1.8 JPython
JyPython este un port pentru Java care permite ca dintr-un script Python să se acceseze fără
nici un effort bibliotecile de clase Java. Mai multe resurse la: www.jython.org.
20.2 Tkinter
20.2.1 Prezentare generală tkinter
Pentru a crea o aplicație de tip GUI utilizând tkinter trebuie să se parcurgă următorii pași:
- Se importă modulul tkinter.
- Se crează o aplicație fereastră pricipală (main Window).
- Se adaugă unul sau mai multe elemente de control widgets în aplicația GUI.
- Se crează bucla principală de evenimente care preia și tratează orice acțiune declanșată de
utilizator.
Tkinter gestionează geometria unei interfețe prin utilizarea mai multor metode.
- Metoda pack() – organizează widgeturile în blocuri înainte de a le plasa în widgetul părinte.
- Metoda grid() – organizează widgeturile într-o structură tabelară în widgetul părinter.
- Metoda place() – organizează widgeturile prin plasarea lor într-o poziție dorită, în
widgetul părinte.
Observație. Exemplele următoare se pot rula în IDLE Python, dar nu și în mediile de
dezvoltare care nu sunt configurate pentru tk, de exemplu în Spyder.
Exemplul 20.1 #Se execută în IDLE Python
import tkinter
window = tkinter.Tk()
#…………………
#Secventa de program care introduce si foloseste widget-uri
#…………………
window.mainloop()
266 Interfețe grafice utilizator (GUI)
2. Canvas. Este un cadru (“pânza”) pentru desenarea formelor (linii, cercuri, ovale, poligoane,
dreptunghiuri, etc.).
Sintaxa:
w = Canvas( master, option=value, ... ),
unde master este fereastra părinte, iar options sunt: bd, bg, confine, cursor, height,
highlightcolor, relief, scrolregion, width, xscrollincrement,
xscrollcommand, yscrollincrement, yscrollcommand.
Widgeturile permise de Canvas sunt: arc, image, line, oval, polygon.
Exemplul 20.3 #widgetul Canvas
import tkinter
top = tkinter.Tk()
C = tkinter.Canvas(top, bg="blue", height=250, width=300)
coord = 10, 50, 240, 210
arc = C.create_arc(coord, start=0, extent=250, fill="yellow")
C.pack()
top.mainloop()
Output
3. Checkbutton. Șir de butoane pentru selectarea de opțiuni. Se pot selecta mai multe variante
simultan.
Sintaxa:
w = Canvas( master, option=value, ... ),
Output
4. Entry. Afișează o singură linie de text (un câmp) pentru introducerea valorilor de către
utilizator.
Sintaxa:
w = Entry( master, option, ... )
unde master este fereastra părinte, iar options sunt: bg, bd, command, cursor, font,
exportselection, fg, highlightcolor, justify, relief, selectbackground,
selectborderwidth, selectforeground, show, state, textvariable, width,
xscrollingcommand.
E1 = Entry(top, bd =5)
E1.pack(side = RIGHT)
top.mainloop()
Output
Sintaxa:
w = Frame( master, option, ... )
unde master este fereastra părinte, iar options sunt: bg, bd, cursor, height,
highlightbackground, highlightcolor, highlightthickness, relief, width.
Output
6. Label. Eticheta - este o singură line de text/descriere (titlu) pentru alte widgeturi. Poate să
conțină o imagine.
Sintaxa:
w = Label( master, option, ... )
270 Interfețe grafice utilizator (GUI)
unde master este fereastra părinte, iar options sunt: anchor, bg, bitmap, bd, cursor,
font, fg, height, image, justify, padx, pady, relief, text, textvariable,
underline, width, wraplength.
Output
unde master este fereastra părinte, iar options sunt: bg, bd, cursor, font, fg,
height, highlightcolor, highlightthickness, relief, selectbackground,
selectmode, width, xscrollcommand, yscrollcommand.
Output
271 Interfețe grafice utilizator (GUI)
8. Menubutton. Este o parte a unui meniu drop-down care este afișat permanent pe ecran.
Fiecare Menubutton este asociat cu un widget de tip Menu care afișează lista de alegeri pentru
Menubutton, atunci când utilizatorul selectează cu click.
Sintaxa:
w = Menubutton( master, option, ... )
Output
9. Meniu. Creează mai multe tipuri de meniuri care pot fi utilizate în aplicații: pop-up,
toplevel și pulldown. De asemenea este posibilă utilizarea altor extensii de widget (de
exemplu, OptionMenu) pentru implementarea a noi tipuri de meniuri.
272 Interfețe grafice utilizator (GUI)
Sintaxa:
w = Menu( master, option, ... )
Output
273 Interfețe grafice utilizator (GUI)
unde master este fereastra părinte, iar options sunt: anchor, bg, bitmap, bd, cursor,
font, fg, height, image, justify, padx, pady, relief, text, textvariable,
unfderline, width, wraplength.
Output
11. Radiobutton. Afișează un număr de opțiuni asemănător unei vechi “claviaturi” radio. Se
poate selecta o singură opțiune la un moment dat.
Sintaxa:
w = Message( master, option, ... )
Output
unde master este fereastra părinte, iar options sunt: bg, bd, command, cursor, digits,
font, fg, from_, highlightbackground, highlightcolor, label, length, orient,
relief, repeatdelay, resolution, showvalue, sliderlength, state, takefocus,
tickfocus, tickinterval, to, troughcolor, variable, width.
label.pack()
root.mainloop()
Output
13. Scrollbar. Adaugă posibilitate de derulare pentru alte widgeturi (ca de exemplu list-boxes).
Sintaxa:
w = Scrollbar( master, option, ... )
unde master este fereastra părinte, iar options sunt: activebackground, bg, bd,
command, cursor, elementborderwidth, highlightbackground, highlightcolor,
highlightthickness, jump, orient, repeatdelay, repeatinterval, takefocus,
troughcolor, width.
Output
Sintaxa:
w = Text( master, option, ... )
unde master este fereastra părinte, iar options sunt: bg, bd, cursor, exportselection,
font, fg, height, highlightbackground, highlightcolor, highlightthickness,
insertbackground, insertborderwidth, insertofftime, insertontime,
insertwidth, padx, pady, relief, selectbackground, selectborderwidth,
spacing1, spacing2, spacing3, state, tabs, width, wrap, xscrollcommand,
yscrollcommand.
Widget-urile de tip text beneficiază de trei structuri distincte care sunt de ajutor: Mark, Tab,
Index.
Un marcaj (Mark) este utilizat pentru a indica sau consemna pozițiile dintre două caractere
într-un text dat. Pentru manipularea marcajelor sunt disponibile următoarele metode:
index(mark), mark_gravity(mark [, gravity]), mark_names(), mark_set(mark,
index), mark_unset(mark).
Etichetele (Tag) sunt utilizate pentru a asocia nume la regiuni de text care ușurează modificarea
setările de afișare ale unor zone de text. Pentru manipularea tagurilor sunt disponibile
următoarele metode: tag_add(tagname, startindex[, endindex] …), tag_config,
tag_delete(tagname), tag_remove(tagname [,startindex [, endindex]]…).
Output
15. Toplevel. Fereastră container care poate conține toate celelalte ferestre (este parent
window) ale aplicației (aplicațiilor, dacă sunt mai multe). Se utilizează in general pentru
afisarea de informații suplimentare ale utilizatorului.
Sintaxa:
w = Toplevel(master, option, ... )
277 Interfețe grafice utilizator (GUI)
unde master este fereastra părinte, iar options sunt: bg, bd, cursor, class_,
font, fg, height, relief, width.
Metodele sunt: deiconify(), frame(), group(window), iconify, protocol(name,
function), state(), transient([master]), withdraw(), maxsize(width, height),
minsize(width, heigth), positionfrom(who), resizable(width, height),
sizefrom(who), title(string).
16. Spinbox. Este o variantă a widgetului Enter, care poate fi utilizat pentru selectarea unui
număr fix de valori.
Sintaxa:
w = Spinbox(master, option, ... )
unde master este fereastra părinte, iar options sunt: activebackground, bg, bd,
command, cursor, disablebackground, disableforeground, fg, font, format,
from_, justify, relief, repeatdelay, repeatinterval, state, textvariable,
to, validate, validatecommand, values, vcmd, width, wrap, xscrollcommand.
mainloop()
Output
17. PanedWindow. Este un container care poate conține un număr de paneluri, aranjate
orizontal sau vertical. Fiecare panel conține un widget și fiecare pereche de paneluri este
separată printr-o bară (sash) deplasabilă prin mișcări ale mouse-ului. Mișcarea unui sash face
ca widgetul de cealaltă parte a sashului să fie redimensionat.
Sintaxa:
w = Spinbox(master, option, ... )
unde master este fereastra părinte, iar options sunt: bg, bd, borderwidth, cursor,
handlepad, handlesize, height, orient, relief, sashcursor, sashrelief,
sashwidth, showhandle, width.
18. LabelFrame. Este o variantă mai simplă a widgetului Frame. Poate fi utilizat pentru
trasarea unei borduri etichetate pentru entități copil, sau poate fi utilizat ca Container pentru
widgeturi înrudite.
Sintaxa:
w = LabelFrame(master, option, ... )
unde master este fereastra părinte, iar options sunt: bg, bd, cursor, font, height,
labelAnchor, highlightbackground, highlightcolor, highlightthickness,
relief, test, width.
279 Interfețe grafice utilizator (GUI)
Output
280 Interfețe grafice utilizator (GUI)
2. Culorile
Reprezentare hexazecimală: roșu, verde, albastru (RGB). ”#000000” = negru, ”fff” = alb.
Opțiuni de culoare: activebackground, activeforeground, background,
disableforeground, foreground, highlightbackground, highlightcolor,
selectbackground, selectforeground.
3. Fonturile
Reprezentare prin tupluri: (”Times”,”18”, ”bold italic”).
Reprezentare prin obiecte: importare tkFont și creare obiect, astfel:
import tkFont
font = tkFont (option, …), unde opțiunile sunt: family, size, weight, slant,
underline, overstrike. Exemplu:
helv24 = tkFont.Font(family = ”Helvetica”, size = 24, weight = ”bold”)
Reprezentare prin fonturi Windows: se utilizează numele fomntului.
4. Ancorele
Ancorele definesc locul unde se poziționează textul în raport cu un punct de referință. Se
identifică prin constantele desemnând direcții cardinale: NW, N, NE, W, CENTER, E, SW,
S, SE. Exemplu: dacă se utilizează CENTER ca ancoră de text, textul va fi centrat orizontal și
vertical în jurul punctului de referință.
5. Stilurile în relief
Stilul în relief al unui widget se referă la efectele simulate 3D în jurul acestuia. Atributele
utilizate pentru crearea efectelor sunt: FLAT, RAISED, SUNKEN, GROOVE, RIDGE:
281 Interfețe grafice utilizator (GUI)
6. Bitmap-urile
Atributele corespund unor bitmap-uri care afișează unele simboluri, coespunzând denumirilor:
"error", "gray75", "gray50", "gray25", "gray12", "hourglass", "info",
questhead", "question", "warning"
7. Cursoarele
• Tkinter permite utilizarea unor cursoare de diferite forme și aspecte, în funcție de sistemul
de operare. Atributele corespund unor bitmap-uri care afișează unele simboluri, coespunzând
denumirilor: "arrow", "circle", "clock", "cross", "dotbox", "exchange",
"fleur", "heart", "heart", "man", "mouse", "pirate", "plus", "shuttle",
"sizing", "spider", "spraycan", "star", "target", "tcross", "trek", "watch".
20.3.2 Să se scrie un program pentru introducerea unui text folosind Button, Entry si Label.
R20.3.2
from tkinter import *
root = Tk()
e = Entry(root, width=40)
e.pack()
e.insert(0, "Introduceti numele: ") #prompter pentru entry
def myClick():
myLabel = Label(root, text="Salut! " + e.get())
282 Interfețe grafice utilizator (GUI)
myLabel.pack()
myButton = Button(root, text="Scrieti numele", command = myClick)
myButton.pack()
root.mainloop()
Output
20.3.3 Să se scrie un program care să ajute la calcule vânzătorul din piața de fructe și legume.
Programul va calcula câștigul vânzătorului (sau pierderea) prin diferența între suma cheltuită
la achiziționarea unei cantități de produse cu un preț și suma obținută prin vânzare la alt preț.
R20.3.3
from tkinter import *
def intro_date():
produs=e1.get()
pret_achiz=e2.get()
pret_vanz=e3.get()
root=Tk()
label=Label(root, text="Cantitate produse").grid(row=0)
label2=Label(root,text="Pret achizitie").grid(row=1)
label3=Label(root,text="Pret vanzare").grid(row=2)
e1=Entry(root,bg='white')
e2=Entry(root,bg='white')
e3=Entry(root,bg='white')
e1.grid(row=0,column=1)
e2.grid(row=1,column=1)
e3.grid(row=2,column=1)
button=Button(root, text='calculeaza', command=intro_date)
button.grid(row=3,column=1)
clearb=Button(root, text="sterge",command=stergere)
clearb.grid(row=3,column=0)
listb=Listbox(root,bg='light blue')
listb.grid(row=4,column=1, padx=20,pady=20)
root.mainloop()
Output
283 Interfețe grafice utilizator (GUI)
20.3.4 Să se scrie un program care să efectueze cele patru operații aritmetice elementare.
R20.3.4
#Un calculator pentru cele 4 operații aritmetice elementare
from tkinter import *
root = Tk()
root.title("Calculator aritmetic")
e = Entry(root, width=40, borderwidth=5)
e.grid(row=0, column=0, columnspan=3, padx=10, pady=10)
# b = “buton”
def b_click(cifra):
current = e.get()
e.delete(0,END)
e.insert(0,str(current) + str(cifra))
def b_sterge():
e.delete(0,END)
def b_adunare():
primul_numar = e.get()
global f_num
global math
math = "adunare"
f_num = int(primul_numar)
e.delete(0, END)
def b_egal():
al_doilea_numar = e.get()
e.delete(0, END)
if math == "adunare":
e.insert(0, f_num + int(al_doilea_numar))
if math == "scadere":
e.insert(0, f_num - int(al_doilea_numar))
if math == "inmultire":
e.insert(0, f_num * int(al_doilea_numar))
if math == "impartire":
e.insert(0, f_num / int(al_doilea_numar))
def b_scadere():
primul_numar = e.get()
global f_num
global math
math = "scadere"
f_num = int(primul_numar)
e.delete(0, END)
def b_inmultire():
primul_numar = e.get()
global f_num
284 Interfețe grafice utilizator (GUI)
global math
math = "inmultire"
f_num = int(primul_numar)
e.delete(0, END)
def b_impartire():
primul_numar = e.get()
global f_num
global math
math = "impartire"
f_num = int(primul_numar)
e.delete(0, END)
# Definire butoane
B_1 = Button(root,text="1",padx=40,pady=20,command = (lambda: b_click(1)))
b_2 = Button(root,text="2",padx=40,pady=20,command = (lambda: b_click(2)))
b_3 = Button(root,text="3",padx=40,pady=20,command = (lambda: b_click(3)))
b_4 = Button(root,text="4",padx=40,pady=20,command = (lambda: b_click(4)))
b_5 = Button(root,text="5",padx=40,pady=20,command = (lambda: b_click(5)))
b_6 = Button(root,text="6",padx=40,pady=20,command = (lambda: b_click(6)))
b_7 = Button(root,text="7",padx=40,pady=20,command = (lambda: b_click(7)))
b_8 = Button(root,text="8",padx=40,pady=20,command = (lambda: b_click(8)))
b_9 = Button(root,text="9",padx=40,pady=20,command = (lambda: b_click(9)))
b_0 = Button(root,text="0",padx=40 pady=20,command = (lambda: b_click(0)))
b_adunare = Button(root,text="+",padx=39,pady=20,command= b_adunare)
b_egal = Button(root,text="=",padx=91,pady=20,command= b_egal)
b_sterge = Button(root,text="Clear",padx=79,pady=20,command= b_sterge)
b_scadere = Button(root,text="-",padx=39,pady=20,command= b_scadere)
b_inmultire = Button(root,text="*",padx=39,pady=20,command= b_inmultire)
b_impartire = Button(root,text="/",padx=39,pady=20,command= b_impartire)
# Afisare butoane pe ecran
b_1.grid(row=3, column=0)
b_2.grid(row=3, column=1)
b_3.grid(row=3, column=2)
b_4.grid(row=2, column=0)
b_5.grid(row=2, column=1)
b_6.grid(row=2, column=2)
b_7.grid(row=1, column=0)
b_8.grid(row=1, column=1)
b_9.grid(row=1, column=2)
b_0.grid(row=4, column=0)
b_sterge.grid(row=4, column=1, columnspan=2)
b_adunare.grid(row=5, column=0)
b_egal.grid(row=5, column=1, columnspan=2)
b_scadere.grid(row=6, column=0)
b_inmultire.grid(row=6, column=1)
b_impartire.grid(row=6, column=2)
root.mainloop()
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 21
Forma de calcul de mai sus, efectuată în Python este dezavantajoasă prezentând un consum
mare de timp pentru liste foarte mari (milioane de numere).
Observație. În limbajul C/C++ viteza de calcul este mai mare, dar sintaxa este incomodă din
cauza relativei complexități a codului, care deranjează pentru un număr mai mare de operații
cu tablouri. Pentru ilustrarea afirmației, se prezintă echivalentul în C/C++ al programului de
mai sus:
for(i=0; i<rows; i++) {c[i] = a[i]*b[i]}
Output
[ 4 10 18]
Avantajele modului de lucru vectorizat din Numpy, față de modul cu utilizare de bucle sunt:
- mai concis și mai ușor de citit;
- mai puține linii de cod, implicit mai puține erori;
- notație mai apropiată de cea matematică.
Observație. Modul de lucru vectorizat în Numpy este doar la suprafață; în fundal, ascuns, la
implementare există totuși bucle repetitive.
288 Numpy
Observație. În Numpy, ndarray este o clasă (OOP) care posedă numeroase atribute și metode,
ce se pot obține cu comanda dir(ndarray), prezentate în lista următoare.
['T', '__init_subclass__ '__setattr__',
'__abs__', ', '__setitem__', 'itemset',
'__add__', '__int__', '__setstate__', 'itemsize',
'__and__', '__invert__', '__sizeof__', 'max',
'__array__', '__ior__', '__str__', 'mean',
'__array_finalize__', '__ipow__', '__sub__', 'min',
'__array_function__', '__irshift__', __subclasshook__ 'nbytes',
'__array_interface__', '__isub__', ', 'ndim',
'__array_prepare__', '__iter__', '__truediv__', 'newbyteorder',
'__array_priority__', '__itruediv__', '__xor__', 'nonzero',
'__array_struct__', '__ixor__', 'all', 'partition',
'__array_ufunc__', '__le__', 'any', 'prod',
'__array_wrap__', '__len__', 'argmax', 'ptp',
'__bool__', '__lshift__', 'argmin', 'put',
'__class__', '__lt__', 'argpartition', 'ravel',
'__complex__', '__matmul__', 'argsort', 'real',
'__contains__', '__mod__', 'astype', 'repeat',
'__copy__', '__mul__', 'base', 'reshape',
'__deepcopy__', '__ne__', 'byteswap', 'resize',
'__delattr__', '__neg__', 'choose', 'round',
'__delitem__', '__new__', 'clip',
'__dir__', '__or__', 'compress', 'searchsorted',
'__divmod__', '__pos__', 'conj', 'setfield',
'__doc__', '__pow__', 'conjugate', 'setflags',
'__eq__', '__radd__', 'copy', 'shape',
'__float__', '__rand__', 'ctypes', 'size',
'__floordiv__', '__rdivmod__', 'cumprod', 'sort',
'__format__', '__reduce__', 'cumsum', 'squeeze',
'__ge__', '__reduce_ex__', 'data', 'std',
'__getattribute__', '__repr__', 'diagonal', 'strides',
'__getitem__', '__rfloordiv__', 'dot', 'sum',
'__gt__', '__rlshift__', 'dtype', 'swapaxes',
'__hash__', '__rmatmul__', 'dump', 'take',
'__iadd__', '__rmod__', 'dumps', 'tobytes',
'__iand__', '__rmul__', 'fill', 'tofile',
'__ifloordiv__' '__ror__', 'flags', 'tolist',
'__ilshift__', '__rpow__', 'flat', 'tostring',
'__imatmul__', '__rrshift__', 'flatten' 'trace',
'__imod__', '__rshift__', 'getfield', 'transpose',
'__imul__', '__rsub__', 'imag', 'var',
'__index__', '__rtruediv__', 'item', 'view']
'__init__', '__rxor__',
Mertoda de creare explicită a unui tablou din liste sau tuple folosind funcția np.array()este
cea mai frecventă.
Exemplul 21.3 #crearea tablourilor din liste sau tuple utilizând np.array()
import numpy as np
a = np.array ([2, 3, 4]) #creare din lista
a1 = np.array (([2, 3, 4]), dtype = np.float64) #creare din tuplu
print(a.dtype)
print(a1.dtype)
c = np.array ([[1,2],[3,4]], dtype = complex)
print(c.shape)
Output
int32
float64
(2, 2)
Observație. Dacă numărul indecșilor este mai mic decât numărul de axe, se consideră indecșii
lipsă ca fiind (slice) compleți (parcurg toate elementele), ca în exemplul care urmează.
Exemplul 21.10 #indexare cu număr mai mic de indecși
>>> c[-1]
array([ 6, 9, 12, 15, 18, 21])
Indexul -1 corespunde ultimului rând, iar de pe ultimul rând s-au parcurs toate elementele.
Observație. Pentru completarea unui tuplu de indexare se poate utiliza simbolul dots (...). De
exemplu, dacă z este un array cu 4 axe, atunci z[1, …] este echivalent cu z[1, :, :, :].
Observație. Iterarea unui tablou multidimensional se face în raport cu prima axă.
Exemplul 21.11 #iterarea se face după prima axă
>>> for row in c:
print(row)
[ 0 3 6 9 12 15]
[ 2 5 8 11 14 17]
[ 4 7 10 13 16 19]
[ 6 9 12 15 18 21]
6
9
12
15
.......
6
9
12
15
18
Observație. Atât reshape, cât și newaxis pot adăuga o nouă dimensiune unui array.
Exemplul 21.18 #reshape și newaxis adaugă o axă nouă unui array
>>> z=np.array([1.,2.,3.,4.])
>>> z.shape
(4,)
>>> z.reshape(2,2) #reshape adaugă o axă nouă
array([[1., 2.],
[3., 4.]])
>>> z
array([1., 2., 3., 4.])
>>> w = np.array([5., 6., 7., 8.])
>>> w.shape
(4,)
>>> w1 = w[np.newaxis, ...]
>>> w1.shape
(1, 4)
>>> w2=w1[np.newaxis, ...]
>>> w2.shape
(1, 1, 4)
[8., 8.]])
>>>np.vstack((ta, tb))
array([[3., 3.],
[3., 8.],
[6., 1.],
[8., 8.]])
>>>np.hstack((ta,tb))
array([[3., 3., 6., 1.],
[3., 8., 8., 8.]])
Un tablou mai mare poate fi divizat în unele de dimensiuni mai mici, fie pe orizontală, folosind
funcția hsplit, fie pe verticală, folosind funcția vsplit. Pentru a se preciza axa pentru care se
dorește diviziunea, se utilizează funcția array_split.
Exemplul 21.20 #splitarea tablourior
>>> x = np.floor(10*np.random.random((4,12)))
>>> x
array([[7., 5., 8., 7., 3., 5., 4., 2., 7., 6., 3., 4.],
[9., 0., 8., 5., 5., 7., 7., 9., 4., 5., 9., 5.],
[1., 5., 3., 0., 4., 7., 7., 3., 5., 4., 4., 6.],
[1., 8., 3., 0., 7., 3., 2., 7., 9., 3., 7., 4.]])
>>> np.hsplit(x,3)
[array([[7., 5., 8., 7.],
[9., 0., 8., 5.],
[1., 5., 3., 0.],
[1., 8., 3., 0.]]), array([[3., 5., 4., 2.],
[5., 7., 7., 9.],
[4., 7., 7., 3.],
[7., 3., 2., 7.]]), array([[7., 6., 3., 4.],
[4., 5., 9., 5.],
[5., 4., 4., 6.],
[9., 3., 7., 4.]])]
>>> np.vsplit(x,2)
[array([[7., 5., 8., 7., 3., 5., 4., 2., 7., 6., 3., 4.],
[9., 0., 8., 5., 5., 7., 7., 9., 4., 5., 9., 5.]]), array([[1., 5.,
3., 0., 4., 7., 7., 3., 5., 4., 4., 6.],
[1., 8., 3., 0., 7., 3., 2., 7., 9., 3., 7., 4.]])]
>>> id(x)
1549550520520
>>> id(y)
1549550520520
Se observă că x și y au același identificator de obiect.
Observație. Dacă tabloul original este șters din memorie cu del, dar referința nu a fost ștearsă,
datele vor rămâne în continuare accesibile prin referință.
Exemplul 21.22 #accesul la date în memorie prin referință, dacă originalul a fost șters
>>> x
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> w = x
>>> del x
>>> x
Traceback (most recent call last):
. . . . . .
x
NameError: name 'x' is not defined
>>> w
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
Modificând forma tabloului “vedere” (view), se modifică și forma tabloului original. Totuși,
dacă se modifică conținutul tabloului view, se modifică și conținutul tabloului original, ceea
ce arată că tabloul view nu este o copie distinctă a tabloului original:
>>> y.shape = 2,5
>>> y.shape
(2, 5)
>>> x.shape
(10,)
296 Numpy
Observație. Și modificările făcute asupra unor obiecte view create prin feliere din tabloul
original modifică datele din tabloul original.
Observație. Dacă nu se dorește copierea întregului tablou, ci numai a unei părți obținute prin
slicing, copierea va fi de asemenea efectivă.
4 5 6 + 1 2 3 => 5 7 9
7 8 9 1 2 3 8 10 12
R2. Dimensiunea fiecarei axe a formei de ieșire este maximul dimensiunilor de intrare pentru
acea axă.
Exemplul 21.28
K (tablou 4D) : 8x1x6x1 #dimensiuni la intrare
M (tablou 3D): 7x1x5 #dimensiuni la intrare
Rezultat 8x7x6x5 #dimensiuni la ieșire (max.din intrări)
R3. Un tablou de intrare poate fi utilizat în calcul, dacă dimensiunea sa de date pentru o axă se
potrivește cu dimensiunea de ieșire de date a axei, sau valoarea sa este exact 1.
Exemplul 21.29
K (tablou 4D) : 8x6
M (tablou 1D): 1
Rezultat 8x6
sau
K (tablou 4D) : 8x6
M (tablou 1D): 6
Rezultat 8x6
R4. Dacă un tablou de intrare are dimensiunea axei egală cu 1, prima intrare de date din acea
axă este utilizată pentru toate calculele de-a lungul acelei axe.
Exemplul 21.30
>>>np.array([1, 2, 3]) + 1
array([2, 3, 4])
298 Numpy
1 2 3 + 1 1 1 =>
2 3 4
Se spune că un set de tablouri poate fi difuzat (este “broadcastabil”) dacă regulile de mai sus
produc un rezultat valid și una dintre următoarele aserțiuni este adevărată:
i. Tablourile au exact aceeași formă.
ii. Tablourile au același număr de axe, iar lungimea fiecărei axe este fie comună, fie 1.
K (tablou 3D) : 15 x 3 x 5
M (tablou 3D): 15 x 1 x 5
Rezultat 15 x 3 x 5
iii. Tabloul care are prea puține axe își va incrementa forma cu o axă de lungime 1, astfel încât
proprietatea menționată mai sus este adevărată.
K (tablou 3D) : 15 x 3 x 5
M (tablou 3D): 3 x5
Rezultat 15 x 3 x 5
Observație. Pentru operațiile elementwise (între elemente, element cu element), din condițiile
prezentate mai sus, se deduce că în cazul tablourilor, dimensiunile sunt compatibile dacă:
1. Numărele de elemente pe axă sunt egale, sau
2. Unul din tablouri este 1.
Exemplul 21.31 #a (2D, 4x3) și b (1x3) au dimensiuni compatibile
import numpy as np
a = np.array([[0.0,0.0,0.0],[10.0,10.0,10.0],[20.0,20.0,20.0],
[30.0,30.0,30.0]])
b = np.array([1.0,2.0,3.0])
print ('Tabloul T1:' )
print (a)
print ('Tabloul T2:' )
print (b)
print ('Suma T1 + T2')
print (a + b)
Output
Tabloul T1:
[[ 0. 0. 0.]
[10. 10. 10.]
[20. 20. 20.]
[30. 30. 30.]]
Tabloul T2:
[1. 2. 3.]
Suma T1 + T2
[[ 1. 2. 3.]
[11. 12. 13.]
[21. 22. 23.]
[31. 32. 33.]]
b = np.array([1.0,2.0,3.0], [50.,50.,50.])
print ('Tabloul T1:' )
print (a)
print ('Tabloul T2:' )
print (b)
print ('Suma T1 + T2')
print (a + b)
Output
Traceback (most recent call last):
. . . . .
b = np.array([1.0,2.0,3.0], [50.,50.,50.])
TypeError: Field elements must be 2- or 3-tuples, got '50.0'
- Restul de axe se imprimă de sus în jos, secționând în secvențe cîte axe reprezintă restul.
Între secvențe se inserează câte o linie vidă.
Exemplul 21.34 #imprimare vector (1D)
import numpy as np
a = np.arange(12)
print(a)
[ 0 1 2 3 4 5 6 7 8 9 10 11]
În cazul tablourilor de mari dimensiuni, funcția print() omite la listare partea din mijloc a
tabloului.
Exemplul 21.37 #imprimare vector de mare dimensiune
print(np.arange(10000))
[ 0 1 2 ... 9997 9998 9999]
Produsul interior între doi vectori de mărime egală întoarce un singur număr (un scalar). Doi
vectori sunt perpendiculari dacă produsul lor este zero. Se poate face cu funcțiile np.inner()
și np.dot().
Produsul interior/inner se poate face și între două tablouri ndarray. În acest caz, rezultatul este
un tablou.
2. Produsul punct (dot product), sau produsul scalar, este un caz special al produsului interior.
Orice produs punct este un produs interior, dar nu invers.
𝑎11 𝑎12 𝑏11 𝑏12 𝑎 . 𝑏 + 𝑎12 𝑏21 𝑎11 . 𝑏12 + 𝑎12 𝑏22
𝐀𝐁 = [𝑎 𝑎22 ] . [𝑏21 ] = [ 11 11 ], (array 2D)
21 𝑏22 𝑎21 . 𝑏11 + 𝑎22 𝑏21 𝑎21 . 𝑏12 + 𝑎22 𝑏22
Observație. . Produsul punct (scalar) oferă rezultate diferite dacă este aplicat unor obiecte
definite ndarray sau obiecte definite matrix. În cazul obiectelor definite ca ndarray produce
același efect ca operatorul “*” (produsele element cu element = elementwise), ci = ai * bi
În cazul obiectelor definite ca matrix produce același efect ca operatorul funcție np.dot(),
cij = ∑aij ∙bij (produs matricial).
Exemplu. 21.39 #produse între două matrici
import numpy as np
# Matricile ca obiecte ndarray
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [8, 9]])
print("a", type(a))
print(a)
print("\nb", type(b))
print(b)
f = np.inner(a, b)
print ("Produsul inner a doua obiecte ndarray, np.inner(a, b),")
print(f)
# Matricile ca obiecte matrix
c = np.matrix([[1, 2], [3, 4]])
d = np.matrix([[5, 6], [8, 9]])
print("\nc", type(c))
print(c)
print("\nd", type(d))
print(d)
print("\n* operatia a*b cu două obiecte ndarray (Elementwise)")
print(a * b)
print("\n* operatia a*b cu două obiecte matrix (rezultat ca cel dat de
np.dot())")
print(c * d)
302 Numpy
g = np.inner(c, d)
print ("Produsul interior a doua obiecte matrix, np.inner(c, d)")
print(g)
Output
a <class 'numpy.ndarray'>
[[1 2]
[3 4]]
b <class 'numpy.ndarray'>
[[5 6]
[8 9]]
Produsul interior a doua obiecte ndarray, np.inner(a, b),
[[17 26]
[39 60]]
c <class 'numpy.matrix'>
[[1 2]
[3 4]]
d <class 'numpy.matrix'>
[[5 6]
[8 9]]
* operatia a*b cu două obiecte ndarray (Elementwise)
[[ 5 12]
[24 36]]
* operatia a*b cu două obiecte matrix (rezultat ca cel dat de np.dot())
[[21 24]
[47 54]]
Produsul interior a doua obiecte matrix, np.inner(c, d)
[[17 26]
[39 60]]
3. Transpusa (transpose), valabilă doar pentru matrici (obiecte 2D), se obține schimbând
liniile cu coloanele. Se poate face cu funcția np.transpose(), sau cu
ndarray.transpose(), sau cu metoda ndarray.T (o metoda specială care nu cere paranteze).
Rezultatul este același în toate cazurile.
4. Urma (trace) este suma elementelor de pe diagonala principală a unei matrice. Se poate
calcula folosind funcția trace(), sau însumând simplu elementele extrase cu metoda
diagonal().
5. Rangul (rank) unei matrici este numărul maxim de vectori coloane sau rânduri liniar
independente din care este compusă. Rangul matricii se poate determina cu funcția
matrix_rank din pachetul linalg din Numpy.
6. Determinantul se poate calcula numai pentru matrici pătrate cu funcția det() din pachetul
linalg. Dacă este zero, atunci matricea este singulară și nu se poate inversa.
7. Inversa (true inverse) unei matrice poate fi calculată numai dacă determinantul său este
diferit de zero. Se utilizează funcția inv() din pachetul linalg.
8. Pseudo-inversa. Chiar dacă determinantul matricii este zero (matricea este singulară), se
poate calcula matricea pseudo-inversă utilizând funcția pinv() din pachetul linalg.
9. Aplatizarea (flatten). În multe aplicații este necesară transformarea unei matrici într-un
vector. Acest lucru se poate face cu ajutorul metodei flatten().
10. Valorile și vectorii proprii. Prin definiție, λ este o valoare proprie (eigenvalue) și x un
vector propriu (eigenvector), dacă este îndeplinită relația: Ax = λX. Vectorii și valorile proprii
sunt importanți în Analiza Componentelor Principale (PCA). În PCA vectorii proprii ai
303 Numpy
>>> A = np.array([[1,2],[3,4]])
>>> B = np.array([[3,1],[5,2]])
>>> A
array([[1, 2],
[3, 4]])
>>> B
array([[3, 1],
[5, 2]])
#operatorul @
>>> A@B
array([[13, 5],
[29, 11]])
>>> A.dot(B)
array([[13, 5],
[29, 11]])
>>> A.transpose() #calculul transpusei unei matrici
array([[1, 3],
[2, 4]])
>>> np.eye(3) #generarea unei matrici unitate
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
>>> np.trace(B) #suma elementelor de pe diagonala principală
5
>>> print("\Produs fct. inner:", np.inner(U, V))
Produs fct. inner: 70
>>> y = np.array([[3.],[2]])
>>> y
array([[3.],
[2.]])
Observație. Operatorii ca += , sau *= acționează asupra aceleiași variabile fără a crea una
nouă. Când se operează cu tipuri diferite, tipul rezultat va fi de tipul cel mai general dintre
operanzi.
Multe operații unare, ca de exemplu calculul sumei tuturor elementelor din array
sunt implementate ca metode ale clasei ndarray.
Exemplul 21.38 #operații implementate ca metode ale clasei ndarray
a = np.random.rand(2, 3)
print(a)
print(a.sum())
print(a.min())
print(a.max())
Output
[[0.96808519 0.33146709 0.58640625]
[0.23741603 0.27951038 0.12903056]]
2.531915496232442
0.1290305582787633
0.968085191684956
>>> np.exp(N)
array([ 1. , 2.71828183, 7.3890561 , 20.08553692])
>>> np.sqrt(N)
array([0. , 1. , 1.41421356, 1.73205081])
>>> M = np.array([-2, -1, -3, -4])
>>> np.add(M, N)
array([-2, 0, -1, -1])
>>> np.sin(N)
array([0. , 0.84147098, 0.90929743, 0.14112001])
>>> np.cos(M)
array([-0.41614684, 0.54030231, -0.9899925 , -0.65364362])
R21.10.1 b
21.10.2 Care este sintaxa corectă pentru verificarea numărului de dimensiuni ale unui tablou?
a. np.dim b. np.ndim c. np.ndim() d. np.dim()
R21.10.2 c
21.10.3 Care este sintaxa corectă pentru imprimarea corectă a numărului 7 din tabloul
următor?
tb = np.array([1,2,3,4], [5,6,7,8])
a. print(tb[3,0]) b. print([2,3]) c. p([1, 2]) d. p([0,3])
R21.10.3 c
21.10.4 Care este sintaxa corectă pentru a imprima numerele [2,3,4] din tabloul următor :
xt = np.array([1,2,3,4,5,6,7,8])
a. print(xt[2:5]) b.print(xt[1:4]) c.print(xt[3:6]) d.print(xt[2:4])
e. print(xt[1:3])
R21.10.4 b
21.10.5 Care este sintaxa pentru imprimarea ultimelor 4 numere din tabloul de mai jos ?
tb = np.array([1,2,3,4,5,6,7,8])
a. print(tb[:4]) b. print(tb[4:]) c. print(tb[5:]) d. print(tb[5:8])
R21.10.5 b
21.10.6 Care este sintaxa care va imprima numerele cu index par din tabloul de mai sus ( tb) ?
a. print(tb[1:3:5:7]) b. print(tb[0:step=2]) c. print(tb[::2])
R21.10.6 c
21.10.7 Care este sintaxa corectă pentru verificarea tipului de dată al unui tablou ?
a. x.type b. X.ntype c. x.dtype d.datatype
R21.10.7 c
21.10.8 Care este sintaxa corectă pentru a crea un tablou de tip float ?
306 Numpy
R21.10.8 b
21.10.9 Ce înseamnă în Numpy forma unui tablou (shape)?
a. numărul de coloane b. numărul de elemente pe fiecare axă c. numărul de rânduri
R2110.9 b
21.10.10 Care dintre următoarele afirmații referitoare la y = x.view() în Numpy este
adevărată ?
a. Vederea y nu este afectată de schimbările în tabloul original x.
b. Vederea y este afectată de schimbările în tabloul original x.
R21.10.10 b
21.10.11 Care este sintaxa corectă pentru obținerea formei tabloului xt ?
a. xt.shape b. shape(xt) c. xt.shape()
R21.10.11 a
21.10.12 Care este metoda corectă pentru lipirea a două tablouri ?
a. join() b. concatenate() c. array_join()
R21.10.12 b, de exemplu: np.concatenate((xt, tb), axis=0)
21.10.13 Care este metoda corectă de căutare a unui element într-un tablou ?
a. search() b. find() c. where() d. searchsorted()
R21.10.13 c, de exemplu:
import numpy as np
b = np.array([1, 2, 2, 3, 4, 5, 2,])
x = np.where(b == 2)
print(x)
Output
array([1, 2, 6], dtype=int64)
21.10.14 Cum se utilizează random în Numpy, pentru obținerea unei distribuții normale de
numere concentrate în jurul lui mu = 60 cu o deviație standard sigma = 0.2 ?
R21.10.14 random.normalvariate(mu = 60, sigma = 0.2)
21.10.15 Care este sintaxa corectă în Numpy pentru adunarea numerelor din tabloul x1 la
numerele din tabloul x2 ?
a. sum(x1, x2) b. np.append(x1, x2) c. np.add(x1, x2)
R21.10.15 a. suma tuturor numerelor din tabloul x1 la suma tuturor numerelor din tabloul x2,
b. concatenează cele două tablouri, c. însumează element cu element cele două tablouri. De
exemplu:
x1 = np.array([1, 2, 3, 4])
x2 = np.array([1, 2, 3, 4])
>>> sum(x1, x2)
array([11, 12, 13, 14])
>>> np.append(x1, x2)
307 Numpy
array([1, 2, 3, 4, 1, 2, 3, 4])
>>> np.add(x1, x2)
array([2, 4, 6, 8])
Output
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
reshape array cu 24 elemente
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[[12 13 14]
[15 16 17]
[18 19 20]
[21 22 23]]]
reshape array cu 25 elemente -> eroare
Traceback (most recent call last):
. . . . . . .
d = c.reshape(2,4,3)
ValueError: cannot reshape array of size 25 into shape (2,4,3)
21.10.17 Care va fi rezultatul adunării următoarelor două array-uri: a de forma (2,3,4), iar b de
forma (2,1,4) ? Explicați.
R21.10.17 Cele două array-uri sunt compatibile în dimensiuni pentru a se aplica procesul
broadcasting, deoarece au aceeași număr de elemente în două dimensiuni, iar pe a treia
dimensiune unul din array-uri are 1 element.
import numpy as np
a = np.arange(24).reshape(2,3,4)
b = np.arange(8).reshape(2,1,4)
c = a + b
print(c.shape)
Output
(2, 3, 4)
21.10.18 Funcția broadcast() este utilă când se dorește să se verifice dacă este posibilă
operațiunea de broadcasting și să se vadă forma sumei a + b fără un calcul explicit. Să se scrie
o secvență de exemplificare.
308 Numpy
R21.10.18
import numpy as np
a = np.empty((4,5,3,2))
b = np.empty((3, 2))
x = np.broadcast(a,b)
print(x.shape)
Output
(4, 5, 3, 2)
Output
[array([1, 2]), array([3, 4]), array([5, 6])]
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 22
Matplotlib
22.1 Generalități
Matplotlib este un modul care conține clase și funcții de creare grafice și figuri, asemănător
stilului Matlab.
Matplotlib a fost creat de John D. Hunter. În majoritate Matplotlib este scris în Python, restul
în C, Objectiv-C și Javascript.
Cel mai important submodul este pyplot, având aliasul plt.
>>>import matplotlib.pyplot as plt
x și y sunt coordonatele punctelor sau nodurilor liniei. Valorile lui x sunt opționale.
Parametrul fmt este opțional și se utilizează pentru definirea culorii, markerului, sau stilului
liniei.
Funcția întoarce o listă de obiecte Line2D reprezentând datele afișate.
Exemplul 22.1 #Utilizarea funcției pyplot.plot() cu crearea automată a figurii și axelor
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0,100.5,0.5) # Generarea valorilor corespunzătoare axei x
y = 2.0*np.sqrt(x) # Calcularea valorilor corespunzătoare axei y
plt.plot(x,y) # Crearea obiectelor figure și axis
plt.show() # Afișarea graficului pe ecran
310 Matplotlib
Output
unde rect este o secvență de float reprezentând dimensiunile cadrului de lucru în care sunt
prezente axele: [l, b, w, h] (left, bottom, width, height).
Exemplul 22.2 #funcția plot cu număr arbitrar de argumente
import numpy as np
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4, 5, 6], [1, 4, 9, 16, 25, 36], 'ro')
plt.show()
Output
Output
311 Matplotlib
Output
Observație. În cazul imprimării unei figuri sau grafic în cadrul unui singur set de axe (2 axe)
se recomandă utilizarea funcției pyplot.plot(), iar în cazul existenței mai multor axe de
coordonate se recomandă utilizarea metodelor pyplot.figure() și figure.add_axes(), sau
similare.
Pentru a lucra cu figurile și axele curente sunt necesare referințe, astfel:
- referința la figura curentă se obține cu pyplot.gfc(), de exemplu: fig = plt.gfg()
- referința la axele curente se obține cu pyplot.gca(), de exemplu: ax = plt.gca().
Plotarea mai multor linii pe un singur set de axe se face prin utilizarea repetată a funcției
pyplot.plot(), sau metodei axes.plot(). Culoarea fiecărui set de linii se selectează
automat, diferit.
Exemplul 22.5 #plotare mai multe grafice (linii) pe un set de axe
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0,100.5,0.5) # Generare valori pentru axa x
y1 = 2.0*np.sqrt(x) # Calcul y1 pentru axa y
y2 = 12*x**(1.0/3.0)+20 # Calcul y2 pentru axa y
y3 = (x)**(1.1) - 7 # Calcul y3 pentru axa y
plt.plot(x,y1) # Plotare y1
plt.plot(x,y2, ls = '--') # Plotare y2
plt.plot(x,y3, ls ='-.') # Plotare y3
plt.show() # Afișare plot pe ecran
Output
313 Matplotlib
Output
Observație. Se poate adăuga un nou plot în aceeași figură prin adăugarea unui nou obiect
axes în același canvas.
Output
Nuanțele de gri pot fi reprezentate prin numere reale cuprinse între 0.0 și 1.0, unde extremele
0.0 reprezintă negru/black și 1.0 reprezintă alb/white.
Alte cuvinte cheie pentru reprezentarea stilului liniilor afișate în grafice sunt : linestyle (sau
ls), linewidth (sau lw), marker, markersize , etc.
Output
Observație. Pentru specificarea rapidă a culorii liniilor și a stilurilor se pot utiliza scurtături,
ca în exemplul care urmează.
Output
317 Matplotlib
Includerea în titluri și etichete a simbolurilor grecești sau matematice se poate face prin
următoarele metode:
- Utilizând sintaxa LaTeX.
- Șirurile matematice trebuie să fie incluse între semne $.
- Șirurile matematice pot fi reprezentate ca șiruri brute (r 'șir').
- Elementele cuprinse între acolade { } sunt grupate împreună.
- Caracterele superscript și subscript sunt definite prin simbolurile '^' și '_'.
- Spațiile sunt inserate prin backslash-slash '\/'.
- Literele grecești sunt redate printr-un backslash urmat de numele literei.
Exemplul 22.11 #metode pentru specificarea etichetelor axelor
318 Matplotlib
http://matplotlib.sourceforge.net/users/mathtext.html#mathtext-tutorial
Pentru stabilirea limitelor axelor se utilizează următoarele variante (min și max sunt limitele
inferioară și superioară ale domeniului axelor):
- precizarea limitelor prin funcții: xlim(min, max), ylim(min, max).
- Precizarea limitelor prin metodele obiectului axe: set_xlim(min, max), set_ylim(min,
max).
Stabilirea poziției diviziunilor și etichetelor se poate face prin funcții și metode având
următoarele argumente:
-loc este o listă sau tuplu conținând pozițiile diviziunilor (ticks).
-lab este o listă sau tuplu opționale conținând etichetele pentru diviziuni.
Observație. loc și lab trebuie să aibă aceeași dimensiune. Pentru fonturi se poate utiliza
size.
Funcții pyplot:
- xticks(loc, lab)
- yticks(loc, lab)
Metode ale axelor:
- set_xticks(loc) și set_xticklabels(lab)
- set_yticks(loc) și set_ytickslabels(lab)
Output
319 Matplotlib
Observație. În cazul în care se utilizează un număr mare de figuri, pentru folosirea eficientă
a memoriei, se recomandă închiderea figurilor neutilizate cu comanda pyplot.close.
Output
Duplicarea axelor este o altă operațiune care prezintă interes frecvent. Axele x și y pot fi
făcute să apară pe partea opusă a figurii grafice utilizând funcțiile twinx() și twiny().
Exemplul 22.14 #duplicare cu păstrarea scalei
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_axes([0.15, 0.1, 0.75, 0.85])
x = np.arange(0.0,100.0)
y = x**3
ax1.plot(x,y)
ax1.set_yticks([0, 250000, 500000, 750000, 1000000])
ax1.set_ylabel('Y (metri)', size = 'large')
320 Matplotlib
ax2 = plt.twinx(ax1)
ax2.set_yticks(ax1.get_yticks())
plt.show()
Output
Output
321 Matplotlib
Artificiul folosit mai sus este utilizarea a două axe diferite care partajează aceeași axă x. Astfel
se pot folosi formateri și locatori matplotlib.ticker separați, axele fiind independente.
Observație. Pentru a genera axe care partajează axa y, dar generează scale diferite sus și jos
(scale x diferite) se utilizează metoda Axes.twiny.
Output
322 Matplotlib
Output
Argument Descriere
numpoints Număr de puncte pentru linia de bordură a legendei
markerscale Raportul mărimii markerelor legendă - figură
frameon True|False, bordura legendei există sau nu
fancybox None|True|False, legenda are sau colțuri rotunde
shadow None|True|False, legenda se afișează cu efect umbră
ncol Număr de coloane pentru legendă
mode ‘expand’|None, extinde legenda orizontal la toată
figura
title Șirul reprezentând titlul
borderpad Număr de spații la interiorul cadrului legendei
labelspacing Spațierea verticală a intrărilor legendei
handlelength Lungimea liniilor din legendă
handletextpad Paddingul între liniile legendei și text
borderaxespad Paddingul între axe și bordura legendei
columnspacing Spațierea orizontală între coloane
323 Matplotlib
Output
theta = np.linspace(0,8*np.pi,100)
r = np.linspace(0,20,100)
plt.polar(theta, r, 'bs--')
plt.show()
Output
Output
325 Matplotlib
Output
Output
Observație. Utilizarea lui transform solicită o instanță obiect axes, astfel că în prealabil
acesta trebuie creat, astfel:
ax = plt.gca()
327 Matplotlib
Output
Observație. Dacă se dorește să se mai adauge o linie în figură, se poate folosi metoda
add_line() a obiectului ax.
Exemplul 22.24 #utilizarea metodei add_line()
import matplotlib.pyplot as plt
import matplotlib.lines as lns #importul functiei lines
import numpy as np
x = np.arange(0, 100.0)
328 Matplotlib
y = 50*np.sin(2*np.pi*x/50.0)
plt.plot(x,y)
ax = plt.gca()
#crearea liniei
l = lns.Line2D((0,25,40, 60,80),(-40, 30,40,-10,20), ls = '--')
ax.add_line(l) #adaugarea liniei
plt.show()
Output
Output
329 Matplotlib
markerfmt='D', bottom=1.1)
markerline.set_markerfacecolor('none')
plt.show()
Output
# liniar
plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('liniar')
plt.grid(True)
# logaritmic
plt.subplot(222)
331 Matplotlib
plt.plot(x, y)
plt.yscale('log')
plt.title('logaritmic')
plt.grid(True)
# simetric logaritmic
plt.subplot(223)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthresh=0.01)
plt.title('Logaritmic simetric')
plt.grid(True)
# logit (logistic)
plt.subplot(224)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logistic')
plt.grid(True)
# Ajustare cadru subplot pentru reglarea reprezentarii logistice
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95,
hspace=0.35,wspace=0.35)
plt.show()
Output
Output
22.14 Reprezentări 3D
Reprezentarea liniilor în 3D
Output
333 Matplotlib
Output
334 Matplotlib
Reprezentarea suprafețelor
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
a = np.arange(-5, 5, 0.35)
b = np.arange(-5, 5, 0.35)
x, y = np.meshgrid(a, b)
z = np.sqrt(5*x**2 + 20*y**2)
ax.plot_surface(x, y, z, rstride=2, cstride=2)
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_zlabel('$z = \sqrt{5*x^{2}+20*y^{2}}$')
ax.set_title('Reprezentare tip suprafață 3D')
plt.show()
335 Matplotlib
22.15.2 Să se creeze 2 figuri diferite, fiecare conținând câte un grid de 2x2 grafice.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0.1, 3 * np.pi)
y1 = np.sin(x); y2 = np.cos(x)
y3 = np.tan(x); y4 = np.sin(x*x)
y5 = np.sin(x*x+8*x);
y6 = np.sin(x)+np.cos(x)
y7 = np.exp(np.sin(x))
y8 = np.cos(np.exp(x)) Figura 1
plt.figure(1)
plt.subplot(221); plt.plot(x, y1, 'ro-')
plt.subplot(222); plt.plot(x, y2, 'ro-')
plt.subplot(223); plt.plot(x, y3, 'rd-')
plt.subplot(224); plt.plot(x, y4, 'rd-')
plt.figure(2)
plt.subplot(221); plt.plot(x, y5, 'b*-.')
plt.subplot(222); plt.plot(x, y6, 'b*-.')
plt.subplot(223); plt.plot(x, y7, 'bx-.')
plt.subplot(224); plt.plot(x, y8, 'bx-.')
plt.show() Figura 2
plt.rcParams["figure.autolayout"] = True
fig, ax = plt.subplots(1)
#Poligonul va avea 6 varfuri (6 perechi x,y)
polygon = Polygon(np.random.rand(6, 2), closed=True, alpha=1)
collection = PatchCollection([polygon])
ax.add_collection(collection)
plt.show()
Output
22.15.4 Functia Rosenbrook (rosen) este utilizată frecvent pentru testarea algoritmilor de
optimizare bazați pe gradienți. Funcția Rosenbrook de două variabile are expresia cea mai
generală: Rosenbrook(x,y)= a*(1-x)**2 + b* ((y-x**2))**2
Frecvent se utilizează valorile a = 1 și b =100. Să se reprezinte grafic funcția Rosenbrook pentru
-5 < x <5 și -5 < y < 5.
R22.15.4
import numpy as np
import matplotlib.pyplot as plot
from matplotlib import cm #color map
from matplotlib.ticker import LinearLocator, FormatStrFormatter
from mpl_toolkits.mplot3d import Axes3D
#functia rosen(x,y)= (1-x)**2 + 100* ((y-x**2))**2
fig = plot.figure()
ax = fig.gca(projection='3d')
s = 0.1 # se incearca 0.05, 0.1, 0.25, s=1
X = np.arange(-5, 5.+s, s)
Y = np.arange(-5, 5.+s, s)
X, Y = np.meshgrid(X, Y)
Z = (1.-X)**2 + 100.*(Y-X*X)**2
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plot.show()
Output
337 Matplotlib
22.15.5 Ce este supertitlul? Cum se asigură distanța suficientă între subploturi? Exemplificați.
R22.15.5 Supertitlul este comun grupului de subploturi. Distanțierea suficientă se asigură cu
atributul layout='tight'în comanda figure().
import matplotlib.pyplot as plt
import numpy as np
#plot 1:
x = np.array([0, 1, 2, 3, 4, 5, 6])
y = np.array([3.6, 3.5, 3.6, 3.7, 3.9, 3.8, 3.9])
plt.figure(layout='tight') #atributul layout='tight' asigura
#distantierea suficienta între subploturi pentru claritate
plt.subplot(1, 2, 1)
plt.grid(axis = 'x')
plt.xlabel("Timpul")
plt.ylabel("Amplitudinea")
plt.plot(x,y, linestyle = 'dashed', marker='o', color='r')
plt.title("Curent")
#plot 2:
x = np.array([0, 1, 2, 3, 4, 5, 6])
y = np.array([39.3, 39.4, 38.6, 38.2, 38.4, 38.3, 38.4])
plt.subplot(1, 2, 2)
plt.grid(axis = 'y')
plt.xlabel("Timpul")
plt.ylabel("Amplitudinea")
plt.plot(x,y, linestyle = 'dotted', marker='d', color='b')
plt.title("Tensiune")
plt.suptitle("Caracteristici electrice")
plt.show()
Output
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 23
Pandas
23.1 Concepte și definiții
Dezvoltarea Pandas a început în 2008 de către Wes McKinney ca răspuns la cererea de creare
a unui nou instrument pentru analiza datelor.
Pandas este o bibliotecă Python dedicată lucrului cu un model de date frecvent întâlnit în
știință, tehnică, economie și alte domenii, denumit “serie de date” .
Corespunzător obiectului “serie de date” (1D), Pandas introduce și tratează și un alt obiect
denumit ”DataFrame” (2D), asemănător unui tablou de date bidimensional, în care prima
coloană este un index, iar celelalte coloane sunt serii de date. De asemenea, se introduce și un
al treilea obiect denumit Panel (3D). Denumirea de Pandas este legată de “Panel Data" .
Motivul pentru care structurile de date de tip Pandas au avut succes se datorează faptului că
este mai ușor de ținut minte și lucrat cu entități denumite “rând”, sau “coloană”, spre
deosebire de “axa 1”, sau “axa 2”, ca în Numpy.
Pandas se instalează automat de unele pachete software ca Anaconda, sau Spyder, dar poate fi
instalat și individual.
Observație. Toate valorile din structurile de date Pandas sunt mutabile, dar seriile ( Series)
sunt imutabile.
Observație. Sistemul de lucru Pandas este foarte puternic și versatil. De exemplu, se pot
realiza cu ușurință prelucrări specifice bazelor de date relaționale, similare cu cele executate
prin intermediul comenzilor SQL.
Datorită capacității de a manipula masive de date, aplicațiile Pandas se regăsesc în special în
domeniile:
- economie,
- sisteme de recomandare (de exemplu: Spotify și Netflix),
- predicția stocurilor la bursele de valori mobiliare,
- neuroștințe, machine learning,
- statistică,
- publicitate,
- analiza datelor,
- Big Data (Hadoop și Spark),
- Data Science,
- Natural Langage Processing (NLP).
Seria de date
Definiție. O serie de date este o secvență de date, cum ar fi o listă în Python, sau un tablou 1D
în NumPy (vector, sau array 1D). Ca și vectorul NumPy, o serie are un singur tip de date, dar
indexarea unei serii este diferită.
În NumPy, controlul, destul de limitat, asupra elementelor unui tablou se face prin indici
numerici pentru rânduri și coloane.
339 Pandas
Într-o serie, ca și în Numpy, fiecare element din serie trebuie să aibă un index unic. Dar, spre
deosebire de Numpy, acest index poate fi însă și altceva decât un număr; poate fi un nume,
sau “o cheie”, în general.
De exemplu, indexul unei serii poate consta din șiruri de caractere reprezentând nume de
localități, elementele corespunzătoare putând fi valori statistice (populația, valoarea
produsului economic, etc.) și numele județului. Un alt caz foarte cunoscut este seria de valori
a unei acțiuni la bursă, pentru care indexul este ziua de tranzacționare (data din calendar).
Seriile de timp sunt frecvent întâlnite; ele sunt mulțimi de valori care apar ca urmare a unei
măsurări sau constatări la momente precizate de timp.
DataFrame
În practică apare frecvent necesitatea prelucrării unor colecții de date grupate și memorate în
fișiere tabelare (cu rânduri având același număr egal de date separate prin virgulă) al căror tip
este denumit CSV (Coma Separated Values).
Definiție. Un DataFrame este o structură de date alcătuită din mai multe serii de aceeași
lungime, având un index comun, legate împreună într-un obiect tabelar, figura 23.1.
Obiectul DataFrame seamănă cu un ndarray 2D Numpy, dar nu este același lucru. Nu toate
coloanele trebuie să aibă același tip de date. Reluând exemplul tabelului în care valorile
indexului sunt localități, coloanele pot fi foarte diferite ca tip de dată: populația (întreg),
producția industrială în ml sau mld euro (real), numele primarului (șir de caractere), sau un
cod boolean indicând statutul de capitală, sau nu, al județului. Această reprezentare din mai
multe tipuri diferite de date este dificil de realizat în Numpy.
De asemenea, fiecare coloană în DataFrame are un nume unic, de regulă un șir de identificare
a informațiilor pe care le conține.
Cu un astfel de obiect DataFrame se pot simplifica stocarea, accesarea, manipularea și se pot
prelucra mai eficient datele unei aplicații.
unde:
- data: ndarray, listă, constante
- index: trebuie să fie unic și hașabil, de aceeași lungime cu datele. Implicit (adică nu e
dat) este np.arange(n)
- dtype: precizează tipul datelor. Dacă nu este dat, va fi dedus.
- copy: copie a datelor. Implicit: False.
În exemplul care urmează se vor crea mai multe serii constând din elemente diferite.
Exemplul 23.1 #Crearea unei serii dintr-o listă, indexare implicită
>>>seria1 = Series([1,2,3,4,5])
>>>print(seria1)
0 1
1 2
2 3
3 4
4 5
dtype: int64
>>>seria2 = Series(['a', 'b', 'c', 'd'])
>>>print(seria2)
0 a
1 b
2 c
3 d
dtype: object
Seriile de mai sus au fost create cu indexul implicit, o valoare întreagă pornind de la 0.
Utilizând argumentul index, se pot eticheta valorile indexului cu nume:
Exercițiul 23.2 #Atribuirea unui index seriei
>>>h = [4, 6, 5] #aici seria este un scalar
>>>seria3 = Series(h, index = ['x', 'y', 'z'])
>>>print(seria3)
x 4
y 6
z 5
dtype: int64
Când o serie se creează dintr-un dicționar, cheia din dicționar devine indexul seriei.
Exercițiul 23.3 #crearea unei serii dintr-un dicționar
import numpy as np
import pandas as pd
from pandas import Series, DataFrame
temperaturi = {"ora8": 5, "ora11": 10, "ora14": 15} #dictionar
seria4 = Series(temperaturi)
print(seria4)
341 Pandas
Output
ora8 5
ora11 10
ora14 15
dtype: int64
Observație. Dacă indexul atribuit este mai scurt decât lista (sau secvența de chei mai scurtă
decât numărul de perechi din dicționar), seria se va scurta corespunzător.
Crearea unui index
Un index poate fi creat independent. În exemplul următor se va crea un index conținând
numele unor localități.
Exemplul 23.4 #Crearea unui index în Pandas
>>>idx_Loc = pd.Index(["Bucuresti", "Timisoare", "Iasi", "Cluj",
"Constanta", "Brasov", "Pitesti", "Craiova"])
>>>print(idx_Loc)
Index(['Bucuresti', 'Timisoare', 'Iasi', 'Cluj', 'Constanta', 'Brasov',
'Pitesti', 'Craiova'], dtype='object')
Indexul obținut ca mai sus poate fi utilizat la crearea unor serii. În exemplul care urmează,
indexul constă dintr-o listă de localități, iar cele trei serii create conțin numărul populației
(conform ro.wikipedia, date pentru anul 2012), județul și altitudinea acelor localități. Pentru
crearea seriilor se vor utiliza ca intrări o listă, și respectiv dicționare.
Exemplul 23.5 #Crearea seriilor având disponibil indexul creat anterior
>>>nr_pop = Series([1898425, 303708, np.nan, 309136, 283872,
253700, 155329, 269506], index =idx_Loc, name =
"Nr_locuitori")
>>>print(nr_pop)
Bucuresti 1898425.0
Timisoare 303708.0
Iasi NaN
Cluj 309136.0
Constanta 283872.0
Brasov 253700.0
Pitesti 155329.0
Craiova 269506.0
Name: Nr_locuitori, dtype: float64
>>>judete = Series({"Pitesti":"AG","Craiova":"DJ","Timisoara":"TM",
"Cluj":"CJ","Brasov":"BV","Constanta":"CT"},name =
"Judet")
>>>print(judete)
Pitesti AG
Craiova DJ
Timisoara TM
Cluj CJ
Brasov BV
Constanta CT
Name: Judet, dtype: object
Cluj 405
Craiova 100
Bucuresti 85
Constanta 25
Name: Altitudine, dtype: int64
În seria Nr_locuitori, în cazul Iașului s-a arătat intenționat că se poate utiliza np.nan pentru
o dată lipsă. Lungimea seriilor este diferită, cu intenție.
Accesarea datelor din serie se poate face prin poziție (similar nd.array), sau prin
eticheta/indexul asociat, fie individual, fie ca listă de elemente.
Exemplul 23.6 #accesarea datelor unei serii
>>>print(seria1[1])
2
>>>print(seria1[:3])
0 1
1 2
2 3
dtype: int64
>>>print(nr_pop['Cluj'])
309136.0
>>>print(nr_pop[['Cluj', 'Brasov']])
Cluj 309136.0
Brasov 253700.0
Name: Nr_locuitori, dtype: float64
unde:
- data: ndarray, serii, liste, dicționare, constante, alt DataFrame
- index: corespunde rândurilor. Dacă nu e precizat, implicit este np.arange(n).
- columns: coloanele (nume), dacă nu sunt precizate va fi implicit nd.arange(n).
- copy: copiere, implicit = False.
2 6 7 8
>>> #Adaugarea etichetelor la randuri si coloane
>>>df = DataFrame(sr, index=['A','B','C'], columns = ['X', 'Y', 'Z'])
>>>print(df)
X Y Z
A 0 1 2
B 3 4 5
C 6 7 8
[3 rows x 9 columns]
De remarcat că valorile NaN se datorează lipsei datelor, deoarece seriile care au fost
combinate au fost de lungime inegală.
Acest DataFrame creat mai sus este dificil de utilizat deoarece are coloanele formate din ceea
ce s-ar dori să fie cheile (indexul), iar rândurile sunt variabilele. Pentru ușurința modului de
procesare dispunerea ar trebui să fie inversată. Acest lucru este posibil fie imprimând tabelul
cu utilizarea opțiunii de transpunere, fie redefinind DataFrame-ul folosind un dicționar, astfel:
Exemplul 23.11 #Crearea DataFrame din serii inegale, cu index, din dictionar
>>>df = DataFrame({"Nr.locuitori":nr_pop,"Judetul":judete, "Altitudinea":
altit})
>>>print(df)
Nr.locuitori Judetul Altitudinea
344 Pandas
Output
Seriile sunt:
Nume: Varsta: Punctaj joc:
0 Ion 10 52
1 George 12 64
2 Emil 15 71
3 Tudor 11 55
4 Petre 12 57
5 Bogdan 14 62
6 Marian 13 63
Transpusa, T: 0 1 2 3 4 5 6
Nume: Ion George Emil Tudor Petre Bogdan Marian
Varsta: 10 12 15 11 12 14 13
Punctaj joc: 52 64 71 55 57 62 63
Etichetele axelor, axes: [RangeIndex(start=0, stop=7, step=1),
Index(['Nume:', 'Varsta:', 'Punctaj joc:'], dtype='object')]
Tipurile de date, pe fiecare coloana, sunt:
Nume: object
Varsta: int64
Punctaj joc: int64
dtype: object
Obiectul DataFrame este vid? : False
Dimensiunea obiectului DataFrame, ndim= 2
Forma obiectului este: (7, 3)
Obiectul este:
Nume: Varsta: Punctaj joc:
346 Pandas
0 Ion 10 52
1 George 12 64
2 Emil 15 71
3 Tudor 11 55
4 Petre 12 57
5 Bogdan 14 62
6 Marian 13 63
Marimea obiectului DataFrame, size= 21
valorile DataFrame :
[['Ion' 10 52]
['George' 12 64]
['Emil' 15 71]
['Tudor' 11 55]
['Petre' 12 57]
['Bogdan' 14 62]
['Marian' 13 63]]
Primele doua randuri:
Nume: Varsta: Punctaj joc:
0 Ion 10 52
1 George 12 64
Ultimele doua randuri:
Nume: Varsta: Punctaj joc:
5 Bogdan 14 62
6 Marian 13 63
Output
Selectare toate randurile pentru o anumita coloana
r1 0.873672
r2 0.505380
r3 0.825728
r4 0.529087
r5 0.720059
Name: c2, dtype: float64
Selectare toate randurile pentru mai multe coloane, date ca lista
c2 c3
r1 0.873672 0.251228
r2 0.505380 0.036395
r3 0.825728 0.575903
r4 0.529087 0.809571
r5 0.720059 0.381581
Selectare cateva randuri, pentru mai multe coloane, date ca lista
c1 c3
r2 0.591144 0.036395
r4 0.650186 0.809571
r5 0.084623 0.381581
Selectare un domeniu de randuri, toate coloanele
c1 c2 c3
r2 0.591144 0.505380 0.036395
r3 0.725653 0.825728 0.575903
r4 0.650186 0.529087 0.809571
afisare tablou de valori logice, rezultate dupa testarea valorilor
c1 True
c2 True
c3 True
Name: r2, dtype: bool
Output
Selectare toate coloanele pentru un interval de randuri
c1 c2 c3 c4 c5
348 Pandas
- Funcția de calcul a covarianței cov() calculează covarianța între obiecte de tip serie de date.
Dacă se aplică unui DataFrame, se calculează între două coloane precizate ale acestuia, sau
pentru toate coloanele, luate două câte două.
- Funcția corr() calculează corelația între două serii. În cazul unui DataFrame, se calculează
corelația între două coloane.
- Funcția reindex() face operația de reindexare, schimbând etichetelor rândurilor și
coloanelor unui DataFrame conform unor cerințe și criterii dorite.
- Operația de iterare a unui obiect depinde de tipul acestuia. Se face de regulă în cadrul unei
bucle for. În cazul seriilor, la fiecare iterație se obține un element, iar în cazul unui
DataFrame se obține un nume de coloană. Iterarea poate fi executată prin intermediul unor
metode: iteritems(), iterrows(), itertuples().
- Sortarea poate fi aplicată etichetelor axelor, sau valorilor elementelor utilizând funcțiile
sortindex() și sortvalues().
În Pandas se poate lucra pe date de tip text. Modificările unui text sunt asemănătoare cu cele
care se fac asupra șirurilor de date, pentru care există foarte multe funcții. Aproape toate
funcțiile dezvoltate pentru lucrul cu șiruri sunt aplicabile și în Pandas pentru lucrul cu datele
de tip text: lower(), upper(), len(), split(), replace(), find(), isupper(),
islower(), strip(),cat(sep=’’) , count(), repeat(), etc.
Observație. Așa cum s-a mai precizat, în Pandas se pot face prelucrări asemănătoare cu cele
permise în bazele de date SQL. De exemplu, comanda join() din Pandas, aplicată unui
DataFrame, poate avea ca parametri: left, right, outer, inner, cu efect asemănător cu
comanda similară din SQL.
De asemenea, se pot găsi multe alte similitudini și secvențe de comenzi Pandas echivalente cu
comenzi SQL (insert, delete, update, alter, etc.), printre care și cu comanda SELECT cu
diferite clauze (where, order by, group by, having, etc.).
Observație. În Pandas există trei metode de aplicare specifică a unei funcții de bibliotecă sau
utilizator. În cele trei cazuri funcția va opera la unul din cele trei niveluri:
- La nivel de tabel (“table wise”) se operează prin pipe(). De exemplu, dacă se dorește
adăugarea valorii 5 la toate elementele din dataframe-ul df, se apelează df.pipe(aduna, 5),
aduna(element1, element2) fiind o funcție utilizator cu doi parametri de intrare și ieșire
suma lor.
- La nivel de coloană sau rând (”row/column wise”) se operează prin apply().
- La nivel de element (”element wise”) se operează prin applymap().
Ferestre mobile
O fereastră mobilă (alunecătoare) este o secvență de valori ale datelor, cuprinzând de regulă
mai multe rânduri alăturate.
Ferestrele sunt de mai multe feluri, cea mai cunoscută fiind “rolling window” . Ferestrele
sunt utilizate de unele funcții ca sum, mean, median, variance, covariance,
correlation.
De exemplu, ”rolling mean” aplicat datelor [1, 2, 34, 4, 5] unde dimensiunea ferestrei este
egală cu 2 produce [1, 1.5, 2.5, 3.5, 4.5]. Dacă fereastra are o lungime mai mare de 1, este
posibil ca unele valori de la începutul secvenței de date rezultate să fie nedefinite (NaN).
350 Pandas
Fișierele .csv sunt utilizate de regulă în Pandas ca mediu intermediar pentru intrările și ieșirile
de date cu alte aplicații, sau pentru stocare.
Pentru citirea datelor din fișierul .csv și conversia în tablou Numpy se utilizează funcția
read_csv, iar pentru salvarea lor se utilizează funcția to_csv().
Output
Nr_crt Nume Vârsta Profesia Salariu
0 1 Ionescu Petre 26 mecanic 3000
1 2 Popescu Alex 34 sofer 2800
2 3 Georgescu Ion 32 electrician 3200
3 4 Radescu George 28 operator 3500
4 5 Toma Andrei 29 paznic 2500
La datele citite se adaugă indexul implicit. Dacă se dorește ca la citire indexul să fie o coloană
din tabloul csv, se utilizează parametrul index_col:
Exemplul 23.17 #citire fișier .csv cu index precizat (ales dintre coloane)
import pandas as pd
df = pd.read_csv("personal.csv", index_col = ["Nr_crt"])
print(df)
Output
Nume Vârsta Profesia Salariu
Nr_crt
1 Ionescu Petre 26 mecanic 3000
2 Popescu Alex 34 sofer 2800
3 Georgescu Ion 32 electrician 3200
4 Radescu George 28 operator 3500
5 Toma Andrei 29 paznic 2500
Se observă că ieșirea afișată conține ca prim rând (nedorit) denumirile coloanelor (headerul)
din fișierul .csv. Pentru eliminarea acestui header, se poate face citirea utilizând argumentul
skiprows, prin care se sar n rânduri specificate.
print(df)
Output
1 Ionescu Petre 26 mecanic 3000
0 2 Popescu Alex 34 sofer 2800
1 3 Georgescu Ion 32 electrician 3200
2 4 Radescu George 28 operator 3500
3 5 Toma Andrei 29 paznic 2500
Output
c1 c2 c3 c4 c5
0 Nr_crt Nume Vârsta Profesia Salariu
1 1 Ionescu Petre 26 mecanic 3000
2 2 Popescu Alex 34 sofer 2800
3 3 Georgescu Ion 32 electrician 3200
4 4 Radescu George 28 operator 3500
5 5 Toma Andrei 29 paznic 2500
Sintaxa:
pandas.data_range(start=None, end=None, peiods=None, freq=None, tz=None,
name=None, **kwargs)
Output
2022-03-01 00:00:00
2022-03-01 04:00:00
2022-03-01 08:00:00
. . . . . . . . . . . . .
2022-03-02 12:00:00
2022-03-02 16:00:00
2022-03-02 20:00:00
2022-03-03 00:00:00
ts = pd.Series(np.random.randn(100),
index = pd.date_range('1/1/2022',
periods = 100))
ts = ts.cumsum()
ts.plot()
plt.show()
353 Pandas
df = pd.DataFrame({'x':
np.random.randn(200) + 1,
'y':
np.random.randn(200),
'z':
np.random.randn(200) - 1},
columns
=['x', 'y', 'z'])
df.plot.hist(alpha = 0.5)
plt.show()
23.9.2 Să se adauge o nouă coloană la un DataFrame în Pandas. În câte moduri se poate face?
R23.9.2 Adăugarea unei noi coloane la un dataframe se poate face prin:
Metoda 1. Declararea unei noi liste drept coloană a df. Lungimea noii liste trebuie să fie egală
cu indexul existent, altfel se va genera eroare.
Metoda 2. Utilizarea metodei DataFrame.insert(). Se oferă libertatea de a adăuga o
coloană în orice poziție, nu numai la sfârșit.
Metoda 3. Utilizarea metodei DataFrame.assign(). Această metodă va crea un nou dicționar
având noua coloană adăugată.
Metoda 4. Utilizarea unui dicționar. Atenție! Se utilizează valorile noii coloane drept cheie,
iar valorile existente vor fi valori în dicționarul creat.
#Datele initiale sunt sub forma unui dictionar de obiecte Series
d = {'Nume':
pd.Series(['Ion','George','Emil','Tudor','Petre','Bogdan','Marian']),
"Varsta": pd.Series([10, 12, 15, 11, 12, 14, 13]),
"Puncte" : pd.Series([52, 64, 71, 55, 57, 62, 63])}
df = pd.DataFrame(d)
#METODA 1
# Declararea unei liste ce va fi convertite intr-o coloana
Localitatea = ['Pitesti', 'Bucuresti', 'Iasi', 'Cluj','Arad', 'Brasov',
'Sibiu']
# Se atribuie numele 'Adresa' ca nume de coloana
df['Adresa'] = Localitatea
print(df)
#METODA 2
# Utilizarea functiei DataFrame.insert() pentru adaugarea unei coloane
df.insert(2, "Sex", ["M","M","M","M","M","M","M"], True)
print('\n',df)
# METODA 3
# Using 'Judet' ca nume de coloana adaugand o lista noua
df2 = df.assign(Judet = ['AG', 'B', 'IS', 'CJ','AR', 'BV', 'SB'])
print('\n',df2)
#METODA 4
Inaltime = {1.45:'Ion',1.71:'George',1.59:'Emil',1.52:'Tudor',
1.58:'Petre',1.68:'Bogdan',1.72:'Marian'}
# Atribuie numele coloanei
df2['Inaltime'] = Inaltime
print('\n',df2)
Output
Nume Varsta Puncte Adresa
0 Ion 10 52 Pitesti
1 George 12 64 Bucuresti
2 Emil 15 71 Iasi
3 Tudor 11 55 Cluj
4 Petre 12 57 Arad
5 Bogdan 14 62 Brasov
6 Marian 13 63 Sibiu
3 Tudor 11 M 55 Cluj
4 Petre 12 M 57 Arad
5 Bogdan 14 M 62 Brasov
6 Marian 13 M 63 Sibiu
23.9.3 Să se creeze un DataFrame Panda dintr-o listă de liste. Lista de liste poate fi o filă din
carnetul de note ale unui elev.
R23.9.3
import matplotlib.pyplot as plt
import pandas as pd
Note = [['Mate', '3.04.22', 9], ['Fizica', '7.04.22', 8], ['Istorie',
'5.04.22', 8], ['Sport', '11.04.22', 10]]
df = pd.DataFrame(Note, columns = ['Disc.', 'Data', 'Nota'])
print(df, '\n')
df = df.transpose()
print("Transpusa df obtinut\n", df)
Output
Disc. Data Nota
0 Mate 3.04.22 9
1 Fizica 7.04.22 8
2 Istorie 5.04.22 8
3 Sport 11.04.22 10
Transpusa df obtinut
0 1 2 3
Disc. Mate Fizica Istorie Sport
Data 3.04.22 7.04.22 5.04.22 11.04.22
Nota 9 8 8 10
23.9.4 Să se creeze un Dataframe din aceeași sursă de date ca la exemplul anterior (carnetul
de note ale unui elev), dar datele sunt privite ca listă de tuple.
R23.9.4
import matplotlib.pyplot as plt
import pandas as pd
Output
Disc. Data Nota
0 Mate 3.04.22 9
1 Fizica 7.04.22 8
2 Istorie 5.04.22 8
3 Sport 11.04.22 10
Output
DataFrame initial
Nume Disc. Data Nota
0 Alex Mate 3.04.22 9
1 Mihai Fizica 7.04.22 8
2 Bogdan Istorie 5.04.22 8
3 Alex Sport 11.04.22 10
4 Mihai Mate 3.04.22 9
5 Alex Fizica 7.04.22 8
6 Bogdan Istorie 5.04.22 8
7 Mihai Sport 11.04.22 10
8 Bogdan Mate 3.04.22 9
9 Mihai Fizica 7.04.22 8
10 Alex Istorie 5.04.22 8
11 Mihai Sport 11.04.22 10
358 Pandas
DataFrame reindexat
Disc. Data Nota
Nume
Alex Mate 3.04.22 9
Mihai Fizica 7.04.22 8
Bogdan Istorie 5.04.22 8
Alex Sport 11.04.22 10
Mihai Mate 3.04.22 9
Alex Fizica 7.04.22 8
Bogdan Istorie 5.04.22 8
Mihai Sport 11.04.22 10
Bogdan Mate 3.04.22 9
Mihai Fizica 7.04.22 8
Alex Istorie 5.04.22 8
Mihai Sport 11.04.22 10
DataFrame sortat
Disc. Data Nota
Nume
Alex Mate 3.04.22 9
Alex Sport 11.04.22 10
Alex Fizica 7.04.22 8
Alex Istorie 5.04.22 8
Bogdan Istorie 5.04.22 8
Bogdan Istorie 5.04.22 8
Bogdan Mate 3.04.22 9
Mihai Fizica 7.04.22 8
Mihai Mate 3.04.22 9
Mihai Sport 11.04.22 10
Mihai Fizica 7.04.22 8
Mihai Sport 11.04.22 10
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 24
SciPy
24.1 Introducere
SciPy (inițiat în 2001 de Travis Oliphant, pe atunci doctorant) este o colecție de algoritmi
matematici și funcții utile construite pe baza bibliotecii NumPy cu scopul de (1) a mări
capacitatea de manipulare, prelucrare și vizualizare a datelor în Python și de (2) a rezolva
probleme științifice și matematice.
Denumirea Scipy derivă din “Scientific Python”. Este o aplicație open source. Cea mai mare
parte a aplicației este scrisă în Python, dar există și părți scrise în C.
Atât NumPy, cât și SciPy sunt biblioteci Python utilizate pentru analiză matematică și numerică.
NumPy conține tipul de date ndarray (inclusiv matrice) și operațiunile de bază cu acesta, cum
ar fi sortarea, indexarea etc., în timp ce SciPy constă din rutine numerice mai complete. Deși
NumPy oferă o serie de funcții care pot ajuta la rezolvarea unor probleme de algebră liniară,
transformări Fourier etc., SciPy este biblioteca având de fapt versiuni dezvoltate ale acestor
funcții și multe altele în plus. În consecință, va fi necesar să se instaleze atât NumPy, cât și
SciPy, deoarece SciPy se bazează pe NumPy.
În consecință, combinația Python + Numpy + Scipy este un concurent puternic, open source,
pentru sisteme consacrate în calculul științific și tehnic ca MATLAB (sistem proprietar),
Octave, R, SciLab, etc.
Pentru concizie și comoditate, se presupune că pachetele principale (numpy, scipy și matplotlib)
au fost importate astfel:
>>>import numpy as np
>>>import matplotlib as mpl
>>>import matplotlib.pyplot as plt
>>>import scipy
Subpachetele Scipy
Pachetul Scipy este constituit din următoarele subpachete corespunzătoare diferitelor domenii
științifice:
cluster – algoritmi pentru clustere
constants – conține constante fizice și matematice
fftpack – funcții de calcul pentru transformate Fourier rapide (vechi)
fft – funcții de calcul pentru transformate Fourier rapide (actualizat)
integrate – rezolvarea ecuațiilor diferențiale și integrale
interpolate – funcții spline pentru interpolareși netezire
io – funcții pentru operații de intrare – ieșire
linalg – funcții de calcul pentru algebra liniară
ndimage – procesarea imaginilor n-dimensionale
odr – metode de regresie distanta ortogonală
360 SciPy
NAME
scipy.special
DESCRIPTION
========================================
Special functions (:mod:`scipy.special`)
========================================
.. currentmodule:: scipy.special
..........................................................
De asemenea, după importul subpachetului se pot obține lista rutinelor și descrierile lor, pe
scurt, folosind comanda dir() și print(scipy.subpachet.__doc__).
from scipy import linalg as lna
dir(lna)
['LinAlgError',
'LinAlgWarning',
'__all__',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
. . . . . . . . . . . . . . . . . . .
'_solve_toeplitz',
'_solvers',
'basic',
. . . . . . . . . . . . . . . . . . .
'cho_solve_banded',
'cholesky',
. . . . . . . . . . . . . . . . . . . .
Observație. Scipy conține un număr imens de rutine care implementează algoritmi și funcții.
Observație. Pentru transferul datelor în și din alte aplicații, scipy utilizează subpacketul io.
Acesta conține rutine de conversie ale fișierelor de date de tip MATLAB (de notat existența
unui subpachet suplimentar scipy.io.matlab), IDL, Matrix Marchet, Fortran, etc. De
asemenea, există rutine de conversie a fișierelur audio de tip wave.
361 SciPy
Output
1000.0
8.0
1.0
0.7071067811865475
Output
(3.9086503371292665, 4.3394735994897923e-14)
Primul argument al funcției quad() de mai sus este funcția de integrat, iar următoarele două
argumente sunt limitele de integrare. Rezultatul este un tuplu cu două valori: prima este
valoarea integrată a funcției, iar a doua este limita de eroare.
Pentru calculul integralei duble (integrala unei funcții de două variabile) se utilizează funcția
dblquad. Argumentele funcției dblquad sunt funcția de integrat și limitele dy (două variabile,
sau două constante) și dx (alte două variabile, sau alte două constante).
Exercițiul 24.3 #integrala funcției de două variabile
from scipy import integrate
a = lambda y, x: (x+1)*y**2+5
b = lambda x: 10
362 SciPy
c = lambda x: -10
print(integrate.dblquad(a, 0, 5, b, c))
Output
(-12166.666666666668, 1.350771346627274e-10)
Scipy oferă și alte funcții pentru calculul integralelor: triple, de ordinul n, etc. etc. Ele pot fi
studiate folosind comanda help().
Output
Matricea inversă:
[[-0.6 0.4]
[ 0.8 -0.2]]
Calculul determinantului:
-5.0
[1,4,6,3],
[2,3,2,5]])
x, y = linalg.eigh(A)
print('valorile proprii:', x)
print('vectorii proprii:', y)
Output
valorile proprii: [-2.53382695 1.66735639 3.69488657 12.17158399]
vectorii proprii: [[ 0.69205614 0.5829305 0.25682823 -0.33954321]
[-0.68277875 0.46838936 0.03700454 -0.5595134 ]
[ 0.23275694 -0.29164622 -0.72710245 -0.57627139]
[ 0.02637572 -0.59644441 0.63560361 -0.48945525]]
24.2.4 Funcții de interpolare
Subpachetul scipy.interpolate pentru realizarea interpolării în SciPy se referă la funcții de
o variabilă (interp1d), sau de mai multe variabile (interp2d).
Exemplul 24.6 #interpolare 1D
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
x = np.arange(5, 20)
y = np.exp(x/2.0)
f = interpolate.interp1d(x, y)
x1 = np.arange(5, 20)
y1 = f(x1)
plt.plot(x, y, 'o', x1, y1, '--')
plt.show()
Output
Output
Transf. Fourier directa: [ 6.-0.j -2.+2.j -2.-0.j -2.-2.j]
Transf. Fourier inversa: [0.+0.j 1.+0.j 2.+0.j 3.+0.j]
Output
Output
368 SciPy
Un alt exemplu poate fi generarea unui semnal modulat în frecvență utilizând funcția
sweep_poly(t, poly, phi=0). Această funcție generează o funcție cosinusoidală a cărei
frecvență instantanee variază în timpul t cu frecvența dată de polinomul poly(t).
Exemplul 24.12 #generarea unui semnal modulat cu funcția sweep_poly
# functia modulatoare a frecventei: p(t) = -1.5*t**2 + 2.25*t + 3
# intervalul 0 <= t <= 6.
import numpy as np
from scipy.fft import fft, fftfreq, fftshift
from scipy.signal import sweep_poly
import matplotlib.pyplot as plt
p = np.poly1d([-1.5, 2.25, 3.0]) #construieste polinomul p(t)
#cu coeficientii -1.5, 2.25 si 3.01
t = np.linspace(0, 6, 5001)
# reprezentarea grafica
plt.figure(1)
plt.plot(t, p(t), 'r', label='f(t)')
plt.legend()
plt.xlabel('t')
plt.title('Reprezentarea polinomului')
plt.figure(2)
plt.plot(t, w)
plt.xlabel('t')
plt.title("Modulare polin. a unei cosinusoide " +
"cu frecv. p(t) = -1.5*t**2 + 2.25*t + 3.01")
plt.figure(3)
#Calcul Transformata Fourier
sp = fftshift(fft(w))
freq = fftshift(fftfreq(t.shape[-1]))
plt.plot(freq, sp, 'b', label='fft')
plt.legend()
plt.xlabel('f')
plt.show()
Output
369 SciPy
Output
Sistemul LIT (liniar și invariant în timp) va fi un filtru Butterworth (răspuns maximum plat în
banda de trecere).
Exemplul 24.13 #răspunul filtrului Butterworth la impulsul unitar și treapta unitară
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
#filtru de ordinul 4
#numarul de esantioane/puncte de calcul al raspunsului n = 20
filtrul_butter = signal.dlti(*signal.butter(4, 0.5))
#butter intoarce functia de transfer ca raport de polinoame
#filtrul_butter este o instanta a functiei de transfer
plt.figure(1)
plt.title('Raspunsul la impuls')
t, y = signal.dimpulse(filtrul_butter, n=20)
plt.step(t, np.squeeze(y))
plt.grid()
plt.xlabel('n=20 [esantioane]')
plt.ylabel('Amplitudine')
plt.figure(2)
plt.title('Raspunsul la treapta')
filtrul_butter1 = signal.dlti(*signal.butter(4, 0.5))
t1, y1 = signal.dstep(filtrul_butter1, n=20)
plt.step(t1, np.squeeze(y1))
plt.grid()
plt.xlabel('n=20 [esantioane]')
plt.ylabel('Amplitudine')
plt.show()
Output
371 SciPy
Datele spațiale constau practic din obiecte care sunt alcătuite din linii, puncte, suprafețe etc.
Pachetul scipy.spatial al SciPy poate calcula diagrame Voronoi, triangulații etc. folosind
biblioteca Qhull. De asemenea, conține implementări KDTree pentru interogări de punct de
tipul cel mai apropiat vecin.
Output
<scipy.spatial.qhull.Delaunay object at 0x000001DEA1AD4EB0>
[[1 2 4]
[5 1 4]
[5 3 2]
[3 5 0]
[1 5 2]]
372 SciPy
R24.3.1
import numpy as np
from scipy.special import comb, perm
k = np.array([2, 3])
n = np.array([4, 4])
#se asociaza pt. calcul: n1-k1, n2-k2, ...
#exact = True => intregi, altfel virgula mobila (float point)
print('permutari n=', n, ' cate k=', k, ':', perm(n, k, exact=False))
print('permutari 4 cate 2:', perm(4, 2, exact=True))
print('combinatii n=', n,' luate cate k=', k, ':', comb(n, k, exact=False))
print('combinatii 4 luate cate 2:', comb(4, 2, exact=True))
print('aranjamente 4 luate cate 2:', comb(4, 2, exact=True, repetition=True))
Output
permutari n= [4 4] cate k= [2 3] : [12. 24.]
permutari 4 cate 2: 12
combinatii n= [4 4] luate cate k= [2 3] : [6. 4.]
combinatii 4 luate cate 2: 6
aranjamente 4 luate cate 2: 10
def function(t):
return t*np.exp(-t/15) + 10*np.sin(t) + (-3*t+5)
t = np.linspace(-17, +2)
fig, ax = plt.subplots()
#ax.plot()
ax.margins(0.2, 0.2) #controlul marginilor
373 SciPy
plt.plot(t, function(t))
plt.show()
#algoritmul de optimizare BFGS
optimize.fmin_bfgs(function, x0=0)
Output
ai cărei coeficienți sunt conținuți în tabloul p = [3.5, -7, 8.2, 2.2, 1],
R24.3.3 Se poate utiliza funcția numpy.roots(p). De asemenea se poate utiliza funcția mai
generală:
scipy.optimize.fsolve(func, x0, args=(), fprime=None, full_output=0,
col_deriv=0, xtol=1.49012e-08, maxfev=0, band=None, epsfcn=None, factor=100,
diag=None),
Output
374 SciPy
24.3.5 Să se genereze o o formă de undă utilizând funcția chirp. Semnalul chirp este un
cosinus cu frecvența modulată.
R24.3.5
import numpy as np
from scipy.signal import chirp
import matplotlib.pyplot as plt
t = np.linspace(5, 15, 500) #timpul
w = chirp(t, f0=4, f1=2, t1=5, method='linear')
#f0 = frecventa la t = 0
#f1 este frecventa la momentul t1
#methode = metoda de baleiere
#alternative la linear: quadratic, logaritmic, hyperbolic
#alti parametri mai sunt: phi = offsetul de faza,
#vertex_zero (pt quadratic)
plt.plot(t, w)
plt.title("Semnal Chirp liniar")
plt.xlabel('Timpul in secunde)')
plt.show()
Output
375 SciPy
SAMPLE_RATE = 50 # Herti
DURATA = 2 # Secunde
# Transformata Fourier
sp = fftshift( fft(y) )
freq1 = fftshift( fftfreq(x.shape[-1]) )
#plt.figure()
#plt.plot(freq, sp.real, freq, sp.imag)
plt.figure(3)
plt.subplot(1,2,1)
plt.plot(freq1, sp.real)
plt.subplot(1,2,2)
plt.plot(freq1, sp.imag)
plt.show()
Output
376 SciPy
Eugen Diaconescu Limbajul și ecosistemul de programare Python
Capitolul 25
În continuare se face referire doar la tipurile de imagine din categoria “raster” sau “bitmap”,
nu și din categoria “vectoriale”. Tipurile “raster” curent utilizate sunt:
Binar. Pixelii imaginii sunt fie albi, fie negri. Fiecărui pixel îi corespunde un bit în memorie.
Grayscale. Fiecare pixel reprezintă o nuanță de gri, codificată cu valori între 0 (negru) și 255
(alb). Fiecare pixel poate fi reprezentat prin 8 biți în memorie. Alte convenții de gri sunt
utilizate în imagistica medicală.
RGB (True Color). Fiecare pixel are o culoare specifică, fiind descrisă de o combinație de
cantități de roșu, verde și albastru. Dacă fiecare componentă de culoare are corespondent un
domeniu 0 – 255, atunci pot fi reprezentate în total 2563 = 16.777.216 de combinații
însemnând culori distincte. Numărul total de biți necesari pentru un pixel este 24; astfel de
imagini sunt denumite “imagini pe 24 de biți”. Imaginea poate fi considerată ca fiind o stivă
de 3 matrice reprezentând roșul, verdele și albastrul.
Indexat. Culorile utilizate în imagine reprezintă doar un mic set selecționat din cele peste 16
milioane de culori posibile. Pentru ușurința memorării și prelucrării fișierelor, imaginile au
asociate o hartă/dicționar de culori (color map, sau color pallete), care este doar o listă simplă
a tuturor culorilor utilizate. Fiecare pixel are o valoare care nu este o culoare (ca pentru o
imagine RGB), ci un index la o culoare din lista de culori specifică acelei imagini.
Pentru simplitate și ușurință este convenabil ca o imagine să conțină 256 sau mai puține
culori, astfel ca indexul să poată fi memorat pe 1 octet. În consecință, a apărut formatul GIF
(și altele) care permite 256 culori sau mai puțin pentru o imagine, obținându-se o dimensiune
redusă a fișierului care conține imaginea.
25.2.2 Convenții de reprezentare a culorilor în imaginile digitale
Spațiul culorilor este o cale de a reprezenta canalele de culoare compunând o imagine. Există
multe spații de culoare și fiecare dintre ele are o structură aparte. Numărul lor mare (peste
100) se datorează faptului că fiecare nou spațiu de culoare apărut a fost destinat unor aplicații
cu cerințe diferite. Dintre acestea, câteva spații de culoare sunt mai frecvent întâlnite: RGB,
CMYK, HSV/HSL, YcrYCb, Lab.
RGB (red, green, blue), utilizat pentru imagini video, este considerat un spațiu de culoare
“aditiv”, culorile fiind imaginate ca produse din proiectarea unor cantități de roșu, verde și
albastru pe un fond negru, figura 2.
Spațiul de culoare RGB se poate reprezenta în 3D ca un cub al culorilor, cu trei axe
ortogonale RGB, figura 1.
Exemple: red = (255, 0, 0), orange = (255, 128, 0), pink = (255, 153, 255).
Problemele convenției RGB sunt amestecul culorilor cu luminanța și perceperea sensibilă a
uniformităților.
379 Elemente de procesare a imaginilor în Python
(1,1,1) white
B
(0,1,1) Cyan (0,0,1) Blue
G
(1,0,1) Magenta
(1,1,0) Yellow
(0,0,0) Green
R
(0,0,0) Black (1,0,0) Red
CMYK (cyan, magenta, yellow, black), utilizat în lumea imprimantelor color, este de
asemenea un spațiu de culoare, culorile fiind considerate proiectate pe un fond alb. Este
considerat un spațiu de culoare “substractiv” deoarece cerneala reține lumina care altfel ar fi
reflectată. De aici provine denumirea de “substractiv”, deoarece cerneala “extrage” culorile
roșu, verde și albastru din lumina albă: (white) minus (red) = cyan, (white) minus (green) =
magenta, (white) minus (blue) = yelow, figura 3.
Fig. 2 Modelul RGB (culori pe fond negru) Fig. 3 Modelul CMYK (culori pe fond alb)
HSV (Hue, Saturation, Value) memorează informația de culoare într-o reprezentare cilindrică
a punctelor RGB de culoare (fig. 4). Reprezentarea în spațiul HSV încearcă să descrie culorile
așa cum sunt percepute de ochiul uman. Valorile Hue sunt între 0 și 179, Saturation între 0 și
255 și Value între 0 și 255. Se utilizează în special în procesarea imaginii, în aplicații de
segmentare, deoarece accentuează contrastul. Se mai consideră: Hue = lungimea de undă
dominantă (dominant wavelength), Saturation = puritatea (gradul de culoare = purity), Value
= intensity.
380 Elemente de procesare a imaginilor în Python
Lab (Lightness, a = (green <---> red), b = (blue <--->yellow)). O imagine codată Lab are un
strat/canal pentru grayscale și împachetate trei straturi de culoare în două straturi. Alte
denumiri pentru Lab sunt CIEL, CIE Lab.
Observație. Spațiul culorilor Lab este destul de diferit de spațiul RGB. În RGB informația de
culoare conține și informația de strălucire. Pe de altă parte, spațiul Lab are canalul L care este
independent de informația de culoare, conținând numai strălucirea.
YCrCb (Y = luminanța, Cr = R – Y, Cb = B – Y). O imagine codată YCrCb separă luminanța
(un strat/canal) de informația de crominanță (două straturi/canale). O altă denumire a acestui
spațiu de culoare este YUV, care s-a păstrat doar ca extensie de fișier corespunzând
formatului.
img = Image.open("image.png")
grayscale = img.convert("L")
grayscale.show()
Adâncimea imaginii
Definiție. Prin “adâncimea imaginii”, sau “adâncimea culorii” (color depth) se înțelege
numărul de pixeli prin care se reprezintă culorile în imagine. De exemplu adâncimea de 1 bit
implică 2 culori, 2 biți implică 4 culori, iar 8 biți implică 256 culori.
Canalul alfa (transparency)
Transparența este o proprietate disponibilă doar în anumite formate grafice: GIF, PNG, BMP,
TIFF, TGA și JPG 2000 sub forma unui canal alfa, sau altfel denumit: “culoare transparentă”.
Celelalte formate grafice ignoră transparența, sau o tratează ca pe un “extra-format”.
Definiție. Canalul de transparență este format din pixeli cărora li se asociază culoarea
fundalului (screen background) pe care sunt plasați. În consecință, pixelii transparenți nu sunt
vizibili pe ecran.
Ca aplicații ale transparenței pot fi date următoarele exemple:
- O imagine care nu este dreptunghiulară poate fi completată pentru a dobândi forma
rectangulară dorită, cu pixeli transparenți. De asemenea, se poate completa o imagine cu
goluri (“găuri”).
- În afișarea unui text, se poate înlocui un caracter nedisponibil cu un simbol transparent, în
locul căruia se afișează culoarea backgroundului.
De asemenea, se poate utiliza și transparența parțială (de exemplu, în formatele PNG, sau
TIFF) pentru un canal alfa. În locul unui pixel care poate fi total transparent, sau total
netransparent, se poate utiliza un set de 254 niveluri de transparență parțială care să permită
trecerea treptată de la fundal (background) la vizibil (foreground).
PNG (Portable Network Graphics) a fost dezvoltat pentru a înlocui și a îmbunătăți formatul
GIF. PNG suportă imagini tip rastru indexate, sau nu, bazate pe o paletă de culori RGB pe 24
biți, sau RGBA pe 32 de biți. De asemenea, suportă imagini grayscale cu sau fără canal alfa
(transparență) și full color non paletă bazat pe RGB sau RGBA.
PNG permite reprezentarea culorilor atât pe 8 cât și pe 16 biți, astfel:
- 24 biți true-color (8 biți pe canal);
- 48 biți true-color (16 biți pe canal);
- 64 biți – adăugarea canalului alfa (transparența).
Spre deosebire de GIF care permite animații, PNG este un format pentru o singură imagine.
Pentru animații a fost creată varianta de format APNG (Animated PNG).
Printre avantajele PNG sunt:
- portabilitatea (referitor la sisteme de operare și browsere);
- reprezentarea imaginilor true-color, inclusiv cu canal alfa, indexate color și grayscale;
- comprimarea fără pierderi, având în consecință o lizibilitatea mai bună a textului și
vizualizarea mai bună a detaliilor.
- transfer progresiv (în browser apare la început o imagine aproximativă care se îmbunătățește
în timp).
Dezavantajul cel mai important este dimensiunea mare a fișierelor pentru imaginile mari cu
foarte multe detalii.
JPEG (Joint Photografic Expert Group), sau JPG, este destinat reprezentării eficiente (cu o
rată mare de compresie), a imaginile tip rastru, mari, sau cu foarte multe detalii, de înaltă
rezoluție. Algoritmul de compresie este cu pierderi. În cazul mai multor etape de decodare –
recodare, pierderile de informație se acumulează, iar calitatea imaginii scade. Este foarte
utilizat în internet, deși comprimarea imaginii cu pierderi îngreunează uneori citirea textului.
Observație. Formatul JPEG nu suportă canalul de transparență și nici animația.
TIFF (Tagged Image File Format). Este un format de imagine rastru complex care poate
suporta aproape toate tipurile de reprezentări de imagine. Deși acceptă compresia cu pierderi,
este de obicei folosit ca format de imagine fără pierderi. Este des utilizat pentru operații în
vederea tipăririi.
# spyder Output
# formatul de imagine = .png
import matplotlib.image as mpimage
image_filename = 'imag.jpg'
im = mpimage.imread(image_filename)
print(im.shape)
import matplotlib.pyplot as plt
plt.imshow(im)
Exemple: Pillow
Modulul Pillow oferă funcțiile open() și show() pentru a deschide și afișa direct pe display o
imagine. Pentru afișarea directă cu show(), Pillow face conversia imaginii în formatul png și
o stochează temporar într-un buffer, apoi o afișează (Metoda I). Dar, prin conversia în
formatul png este posibilă pierderea unor proprietăți ale imaginii, datorită restricțiilor
formatului png. Se recomandă ca această metodă să fie evitată.
Dacă se dorește evitarea afișării cu pierderi, se poate transforma imaginea într-un array
numpy, care apoi va fi afișat cu matplotlib.pyplot (Metoda II).
Exemplul 25.3 # afișarea directă a imaginilor png (Metoda I)
format png
from PIL import Image
img = Image.open("imag1.png")
img.show(img)
image_filename ='mozart.jpg'
mg=Image.open(image_filename)
plt.figure(1)
plt.subplot(141)
#imaginea originala
plt.imshow(mg)
#splitarea in trei imagini grayscale
r,g,b = mg.split()
plt.subplot(142)
plt.imshow(r)
plt.subplot(143)
plt.imshow(g)
plt.subplot(144)
plt.imshow(b)
#fuzionarea in alta ordine a celor trei canale
mg2 = Image.merge('RGB', (g,b,r))
plt.figure(2)
plt.imshow(mg2)
# redimensionarea imaginii
r_img = img.resize(noua_dim, resample = Image.BILINEAR)
# redim_img => Destinatie
r_img.save("redim_img.jpg")
# Open noua imagine
img = Image.open(r"redim_img.jpg")
img.show(img)
print("\nNoua dimensiune a imaginii, dupa salvare")
print(img.size)
Output
Dimensiunea originala a imaginii
(1036, 562)
Output
110.16274388631184 255 0
Exemple: Scikit-image/skimage
Scikit-image (modulul skimage) este o colecție de algoritmi pentru procesarea imaginii, bazat
pe scipy.ndimage.
Se instalează cu comanda:
python -m pip install --user scikit-image
Output
(4000, 3000, 3)
387 Elemente de procesare a imaginilor în Python
Exemple: Opencv-Python
Output
(385, 167, 3)
(550, 1100, 3)
388 Elemente de procesare a imaginilor în Python
Exemplul 25.9
from PIL import Image
img1 = Image.new(mode="RGB", size=(200, 200))
img1.show()
img2 = Image.new(mode = "RGB", size = (200, 200), color = (128, 155, 255))
img2.show()
Output
Output
(1100, 550)/
JPEG
389 Elemente de procesare a imaginilor în Python
Output
RGB
Output
Output
original: latimea: 4000 inaltimea: 3000
Parametri:
mode = reprezintă modul utilizat pentru imaginea de ieșire.
bands = este o secvență conținând câte o singură imagine “single-band” pentru fiecare bandă
din imaginea de ieșire. Toate benzile trebuie să aibă aceeași dimensiune.
Exemplul 25.15 #fuzionarea imaginilor, imaginile fiind chiar benzile de culoare ale
aceleiași imagini
# se importă clasa Image
from PIL import Image
# se creează obiectul
image = Image.open(r"parc_centru.jpg")
image.load()
image.show()
# Se divide imaginea originala in benzi individuale
r, g, b, = image.split()
r.show()
g.show()
b.show()
# fuziunea in diverse ordine a canalelor.
im1 = Image.merge('RGB', (g, b, r))
im1.show()
392 Elemente de procesare a imaginilor în Python
Imaginea originală
Parametri:
imagine1 = prima imagine (destinația).
imagine2 = a doua imagine din compunere, cu aceeași dimensiune ca prima.
masca = o imagine mască. Aceasta poate avea modul I, L, sau RGBA și trebuie să fie de
aceeași dimensiune ca primele două.
Funcția Image.blend() “amestecă” două imagini în mod ponderat.
Sintaxa: PIL.Image.blend(imagine1, imagine2, alfa)
Parametri:
imagine1 = prima imagine (destinația).
imagine2 = a doua imagine din compunere, cu aceeași dimensiune ca prima.
alfa = factorul de interpolare (ponderea imaginilor în “amestec”). Pentru alfa = 0 se
întoarce o copie a primei imagini, pentru alfa = 1 se întoarce o copie a celei de a doua
imagini.
Funcțiile Image.copy() și Image.paste() permit ca o copie a unei imagini să se poată lipi
pe o altă imagine. Dacă modurile celor două imagini nu se potrivesc, atunci imaginea de lipit
se va converti in modul imaginii destinație. În loc de o imagine, sursa poate fi un întreg sau
un tuplu conținând valori de pixeli, asfel atribuind regiunii de lipit o culoare dată.
Sintaxa: Image.paste(imagine, cadrul = None, masca = None)
Parametri:
imagine() = imaginea destinație pentru imaginea sursă obținută cu copy().
cadrul = este None (echivalent cu (0,0)), sau un tuplu dublu precizând colțul stânga sus (sau
cvadruplu, precizând cele patru colțuri), al regiunii în care se va lipi imaginea sursă.
masca = dacă este prezentă, lipirea se va efectua doar în regiunea corespunzătoare măștii. Se
pot utiliza imagini având modurile “1”, “L”, “LA”, “RGBA” or “RGBa”, cu observația că,
dacă este prezentă banda de transparență alfa, aceasta va fi utilizată drept mască. Dacă masca
este 255, imaginea va fi copiată așa cum este, dacă masca = 0, valorile pixelilor nu se
modifică. Dacă valoarea este între 0 și 1, se va face amestecul celor două imagini, inclusiv
canalele alfa dacă sunt prezente.
Parametri: box este un 4-tuplu definind colțurile (left, upper, right, bottom/lower) în
coordonate în pixeli ((x1, y1, x2, y2), unde x1 < x2, y1 <y2) .
image1 = image.convert("RGBA")
image1.show()
benzi1=image1.getbands()
print("Benzi RGBA:", benzi1)
image2 = image.convert("L")
image2.show()
benzi2=image2.getbands()
print("Benzi L:", benzi2)
image3 = image.convert("1")
image3.show()
benzi3=image3.getbands()
print("Benzi 1:", benzi3)
Output
Benzi: ('R', 'G', 'B') Benzi:('R','G', 'B', 'A') Benzi: ('L',) Benzi: ('1',)
25.4.3 În câte moduri se pot detecta muchiile obiectelor din imagini utilizând Python?
R25.4.3 Detectarea muchiilor este o tehnică prin care se determină marginile obiectelor în
cadrul imaginilor. Baza tehnicii este detectarea discontinuității în luminanță.
396 Elemente de procesare a imaginilor în Python
Detectarea muchiilor este utilă deoarece foarte multă informație despre forma obiectelor și
conținutul imaginii se află în muchiile obiectelor din imagine.
Pentru exemplificare, se arată cum funcționează unul dintre cei mai cunoscuți algoritmi de
detectare a muchiilor: algoritmul Sobel. Acest operator este construit din două nuclee simple
de convoluție de 3 * 3 pixeli. Al doilea nucleu Ny este versiunea rotită cu 90o a nucleului Nx.
Ca urmare a aplicării procesului de convoluție se obțin două versiuni ale imaginii:
1 0 −1 1 2 1
𝐺𝑥 = [2 0 −2] × 𝑀𝑎𝑡𝑟𝑖𝑐𝑒𝑎 𝑖𝑚𝑎𝑔𝑖𝑛𝑒 𝐺𝑦 = [ 0 0 0 ] × 𝑀𝑎𝑡𝑟𝑖𝑐𝑒𝑎 𝑖𝑚𝑎𝑔𝑖𝑛𝑒
1 0 −1 −1 −2 −1
Valorile pixelilor din cele două imagini se compun pentru obținerea valorii finale ale
pixelilor, corespunzător gradientului de convoluție 2D:
𝐺 = 𝑠𝑞𝑟𝑡(𝐺𝑥2 + 𝐺𝑦2 )
Deoarece în Python sunt disponibile mai multe biblioteci și aplicații de procesarea imaginilor,
numărul de metode și variante de cod scrise pentru detectarea muchiilor este foarte mare, de
exemplu: Canny, Sobel, Laplacian, Scharr, Prewitt, Roberts. În continuare se prezintă doar
câteva dintr-un număr foarte mare de posibilități.
Metoda I
Algoritmul constă în convoluția imaginii cu un nucleu special de forma unei matrice.
Matricea utilizată de funcția FIND_EDGES din Pillow este următoarea:
( -1, -1, -1,
-1, 8, -1,
-1, -1, -1
)
from PIL import Image
from PIL import ImageFilter
image = Image.open("E:\...\...\...\primarie.jpg") #obiectul imagine
#aplicarea metodei FIND_EDGES de detectare a muchiilor
imageWithEdges = image.filter(ImageFilter.FIND_EDGES)
image.show() #afisare imagine originala
imageWithEdges.show() #afisare imagine cu muchiile detectate
# Detecția muchiile cu FIND_EDGES (Pillow)
Metoda a II-a
import cv2
from skimage import feature, filters, io
397 Elemente de procesare a imaginilor în Python
Capitolul 26
Dintre componentele afișate, unele pot fi lansate direct (launch), iar restul trebuie instalate. De
asemenea, mai pot fi adăgate și alte componente utile dezvoltării aplicațiilor.
Denumirile componentelor vizibile în cadrul de lucru de mai sus, sunt:
- CMD.exe (prompt) – Terminalul linie de comandă al Windowsului, lansat de Anaconda.
- Datalore – Analiză de date online cu asistență inteligentă la codare de la JetBrain.
- IBM Watson Studio Cloud – Instrument profesional pentru IA, utilizat in special pentru
cercetare științifică.
- JupyterLab – instrumentul principal al proiectului Jupiter.
- Jupyter Notebook – varianta inițială (încă utilizată) a proiectului Jupiter.
- PowerShell Prompt – este un terminal PowerShell al sistemului Windows, lansat de
Anaconda.
- QtConsole – Este o consolă compatibilă Qt, urmașă a vechii console IPython (deși numele
sunt foarte similare, Python și IPython sunt lucruri complet diferite. IPython a fost un terminal
în linie de comandă, interactiv pentru Python. I, din IPython provine de la “Interactiv”)).
- Spyder – Scientific Python Development EnviRonment. Mediu de dezvoltare utilizând
Python adaptat pentru cercetarea științifică.
- Glueviz – Este o bibliotecă Python open-sursă pentru analiza legăturilor dintre seturile de
date înrudite.
- Orange3 – Este un sistem open-sursă de vizualizare a datelor și prelucrări de tip machine
learning și data mining. Utilizează programarea vizuală.
- PyCharm Professional – System de dezvoltare avansat pentru utilizarea Python și a altor
procesoare software, inclusiv din ecosistemul Python.
- RStudio – Este un mediu de integrare de dezvoltare (IDE) pentru limbajul R, dedicat
calculului statistic și reprezentărilor sale grafice. RStudio se poate rula fie în browser, fie
desktop.
400 Administrarea ecosistemului Python
set PYTHONPATH=%PYTHONPATH%;C:\cale\bib\srsapp
)
Linia shebang
Caracterele “#!” sunt denumite în jargonul programatorilor ”shebang” sau ”hashbang”. Ele sunt
folosite în general pe prima line a unui program pentru a preciza calea către procesorul software
ce trebuie lansat în execuție.
În cazul Python , linia completă #!/usr/bin/python3 (sau #!/usr/bin/env python3)
indică adresa de lansare a interpretorului Python în sistemul de operare Linux (Ubuntu).
Fișierul care conține linia este considerat un script, iar interpretorul îl va executa.
În Windows linia nu are efect, este interpretată drept un comentariu.
b)
>>>sys.version #afisare versiune Python
'3.10.1 (tags/v3.10.1:2cd268a, Dec 6 2021, 19:10:37) [MSC v.1929 64 bit
(AMD64)]'
c)
#sys. version_info este un tuplu indicând numarul versiunii. Tuplul
#contine cele cinci componente ale numarului versiunii: major, minor,
#micro, releaselevel și serial.
>>>sys.version_info
sys.version_info(major=3, minor=10, micro=1, releaselevel='final',
serial=0)
d)
>>>sys.int_info #informatii despre intregi in Python: max, min, etc.
sys.int_info(bits_per_digit=30, sizeof_digit=4)
e)
>>>sys.implementation #informatii despre implementarea Python
namespace(name='cpython', cache_tag='cpython-310',
version=sys.version_info(major=3, minor=10, micro=1, releaselevel='final',
serial=0), hexversion=50987504)
403 Administrarea ecosistemului Python
f)
>>>sys.byteorder #ordinea octetilor: little endian, big endian
'little'
g)
>>>sys.getsizeof(1234) #dimensiunea in octeți a reprezentarii numarului
28
sys.getsizeof("abcdef") #dimensiunea in octeți a reprezentarii sirului
55
h)
#sys.argv
import sys
print("Acesta este numele programului:", sys.argv[0])
print("Lista de argumente:", str(sys.argv))
Output
Acesta este numele programului: E:/1Python_Work/Test2/ex_tst_argv.py
Lista de argumente: ['E:/1Python_Work/Test2/ex_tst_argv.py']
#funcții os
i)
>>> os.getcwd() #dupa lansare Python
'C:\\Program Files\\Python310'
>>>os.getcwd() #in cadrul sesiunii de lucru
'E:\\1Python_Work\\Test2'
j)
>>os.listdir() #dupa lansare Python
['DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'NEWS.txt',
'python.exe', 'python.pdb', 'python3.dll', 'python310.dll',
'python310.pdb', 'pythonw.exe', 'pythonw.pdb', 'Scripts', 'tcl', 'temp',
'Tools', 'vcruntime140.dll', 'vcruntime140_1.dll']
>>> os.listdir() #in cadrul sesiunii de lucru
['Test3', 'z.py']
k)
>>>os.getpid()
6584
l)
>>>print(os.environ) #Listeaza continutul variabilelor de mediu
environ({'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA':
'C:\\Users\\USER\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program
Files\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files
(x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common
. . . . . . . . . . . . . . . . . . . . . . . . . .
Pentru virtualenv:
>>>virtualenv <DIR>
>>>source <DIR>/bin/activate
Observație. Se mai spune că un mediu virtual este doar “un mediu Python într-un folder”:
Output
['E:/A_Python-work', 'C:\\Program Files\\Python310\\Lib\\idlelib', . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
['E:\1Python_Work\\Test3', 'E:/A_Python-work', . . . . . . . . . . . . . .
. . . . . . . . . . . .
Metoda 2
import sys
sys path.append('adresa completă a directorului de adăugat(incluzând
calea)')
26.5.3 Cum se poate face ștergerea unui folder din variabila căilor de acces ?
R26.5.3
#stergere temporara !
print(sys.path)
sys.path.remove('E:/1Python_Work/Test3')
print(sys.path)
Output
['E:/1Python_Work/Test3', 'E:/A_Python-work', 'C:\\Program
Files\\Python310\\Lib\\idlelib', . . . . . . . . .
26.5.3 Cum se poate afla calea sau numele folderului de lucru curent ?
#A numele folderului unde se află python.exe
>>>import sys
>>>print (sys.executable)
C:\Program Files\Python310\pythonw.exe
#B numele folderului de lucru curent
#metoda 1
>>>import os
>>>print(os.getcwd())
E:\A_Python_work
#metoda 2
>>>import pathlib
>>>print(pathlib.Path(__name__).parent.resolve())
E:\A_Python_work
#Metoda 3
>>>import os
>>>print(os.path.abspath(__name__))
E:\A_Python_work\__main__
#Metoda 4
>>>print(os.path.dirname(__file__))
E:/A_Python-work
Output
Factorialul lui 10 este:
407 Administrarea ecosistemului Python
3628800
Timpul total de executie este = 0.04639744758605957
Factorialul lui 100 este:
933262154439441526816992388562667004907159682643816214685929638952175999932
299156089414639761565182862536979208272237582511852109168640000000000000000
00000000
Timpul total de executie este = 0.04635953903198242
Factorialul lui 1000 este:
402387260077093773543702433923003985719374864210714632543799910429938512398
62902059204420848696940480047998861019719605. . . . . . . . . . . . . .
Timpul total de executie este = 0.1249992847442627
26.5.6 Să se scrie, în sistemul de operare Linux, un script Python pentru a executa un program
C/C++.
R26.5.6 Soluția problemei constă în utilizarea subproceselor. Se scrie mai întâi un program în
C/C++, care se verifică și se execută corect.
#include <iostream>
using namespace std;
int main(){
cout<<"Hello World\n";
return 1;
}
Output
$ python main.py
Hello World
printing result
1
Eugen Diaconescu Limbajul și ecosistemul de programare Python
GLOSAR
Concepte și termeni utili pentru începători (în engleză și română)
A
Algoritm = un set de reguli, bine-definite, pentru soluționarea unei probleme, într-un număr finit de
pași.
Alias = “un alt nume”. O locație de memorie (sau în general o variabilă) poate fi referită de mai mulți
identificatori diferiți, denumiți alias. Dacă una din variabilele alias se modifică prin atribuirea unei
noi valori, atunci se poate ca și ceilalți identificatori alias să-și modifice valoarea.
Application Program Interface (API) = o listă de elemente care definește schema de interacționare
între două aplicații. Componentele listei sunt comune și recunoscute de două sau mai multe aplicații.
Argument = parametru de intrare al unei funcții
B
Batch = mod de lucru în care comenzile Python sunt reunite secvențial într-un fișier text (cu
terminația .py. .cpp, etc.), analizate din punct de vedere sintactic, transformate într-un fișier obiect
(compilate) și apoi sunt executate în grup (etapa run-time). Este un mod de lucru opus interpretării, în
care comenzile sunt executate în secvență, câte una la un moment dat.
Binding = legare, legătură
Bug = o eroare într-un program
C
Calificare = legarea obiectului A de obiectul B prin notația B.A și denumită “B califică A”, sau “A
este calificat de B” (ex.: math.sqrt, math califică sqrt).
Cycle = o cale într-un graf care începe și sfârșește în același nod.
Clasa de memorarare a variabilelor = atribut al unei variabile care determină modul de alocare a
spațiului de memorie și durata de viață corespunzătoare acesteia.
Class (POO) = un prototip definit de utilizator pentru un obiect care definește un set de atribute care
caracterizează obiectul. Atributele sunt date membre (variabile ale clasei și variabile ale instanței) și
metode, accesate prin notația cu punct.
Colon = două puncte (:). Semicolonul este simbolul punct și virgulă (;).
Compilare = proces de conversie a unui modul program din formă sursă în formă obiect binar
executabil de mașină.
Compilator = program software care transformă un program sursă (text) în program obiect (binar).
Numele simbolice din programul sursă sunt înlocuite cu adresele actuale unde sunt memorate datele.
Pentru a deveni executabil, uneori programul obiect mai trebuie să fie legat de alte module binare și
eventual transformat din nou în alt format. În timpul execuției (run-time) rămân de regulă fixate
numele, tipul și adresa locației de memorie, schimbându-se doar datele stocate în locațiile de
memorie.
Concatenare = alipire de șiruri
D
Data member (POO) = o variabilă a clasei sau variabilă a instanței care memorează date asociate cu o
clasă și obiectele sale.
Debugging = procesul de găsire și corectare a unor erori (bugs).
409 Glossar
Docstring = un șir aflat (sau care începe) pe prima linie a definiției unei funcții sau a definiției unui
modul, după antet.
Dot notation = utilizarea operatorului punct (.) pentru accesarea metodelor și atributelor unui obiect,
de exemplu: nume_obiect.nume_atribut.
Dunder = abreviere pentru “double underscore” ( __ ).
E
Expresie = un set de variabile și constante (operanzi) conectate prin operatori.
F
Fișier = colecție de elemente identificabile denumite înregistrări, stocate pe un suport de informație
(de regulă extern).
Fișier text = un fișier conținând caractere organizate pe linii (rânduri) terminate cu un caracter
(newline), sau două (newline, carriage-return).
Fișier binar = fișier ale cărui elemente sunt în reprezentarea internă a calculatorului.
Fișier sursă = un fișier text, scris într-un limbaj de programare.
Floating-point = tipul numeric reprezentând numerele care au parte fracționară și sunt reprezentate în
memoria calculatorului prin convenția “virgulă mobilă”, prin care virgula nu ocupă o poziție fixă.
Funcție = secvență de instrucțiuni denumită și subrutină sau procedură. Primește la intrare un set de
parametri denumiți argumente și întorce de regulă rezultatul execuției prin intermediul unei
instrucțiuni return.
G
Global = se referă la domeniul de vizibilitate al unei variabile, extins atât la interiorul cât și la
exteriorul funcțiilor (domeniul de vizibilitate al variabilei este întreg programul sursă).
Graf = o mulțime V de noduri (valori) și o mulțime E de arce care conectează nodurile.
Gramatică = regulile care definesc un limbaj.
H
Handler = mâner, manetă. De regulă este un număr care se asociază unic cu o entitate software pentru
identificarea acesteia. De exemplu, identificatorul unui fișier stabilit prin instrucțiunea de deschidere a
acestuia.
Handling = tratare, manevrare, prelucrare aplicată unui obiect software.
Header = antet.
Higher order function = funcție de ordin superior. De regulă poate primi ca argument o altă funcție.
Hash function = orice funcție care poate fi utilizată pentru maparea (punerea în corespondență) datelor
de dimensiuni arbitrare la (cu) valori de dimensiuni fixe. Valorile returnate de o funcție hash se
numesc valori hash sau coduri hash, Valorile sunt de obicei utilizate pentru indexarea unui tabel (sau
dicționar) cu dimensiuni fixe numit tabel hash (hashtable). Funcțiile hash și tabelele lor hash asociate
sunt utilizate în aplicațiile de stocare și recuperare a datelor pentru a accesa datele într-un timp mic și
aproape constant de recuperare și necesită o cantitate de spațiu de stocare doar fracțional mai mare
decât spațiul total necesar pentru date sau înregistrări. Hashing-ul este o formă de acces la date,
eficientă din punct de vedere al spațiului de stocare și calcul, care evită timpul de acces neliniar al
listelor ordonate și neordonate și cerințele de stocare adesea exponențiale ale accesului direct. Un
aspect de interes este unicitatea asocierii codurilor hash cu datele.
Hashtabil = un obiect este hashtabil dacă are asociată (există) o funcție care întoarce întotdeauna
aceeași valoare pentru obiectul respectiv.
410 Glossar
Hashtable = tabel de acces la date construit pe baza utilizării unei funcții de hashing pentru generarea
codurilor de acces.
I
Immutable data = valoarea datei nu poate fi modificată (tuple, șiruri).
Index = o valoare întreagă care referă elementele unui tablou sau enumerări.
Inheritance (POO) = moștenire. Transferul caracteristicilor unei clase către alte clase derivate din ea.
Instance (POO) = un obiect concretizat (instanțiat, individualizat, realizat) dintr-o clasă.
Instantiation (POO) = crearea unei instanțe (obiect) a unei clase.
Interpreter = interpretor, procesor software care execută pas cu pas instrucțiunile unui program,
întorcând rezultatul execuției fiecărei instrucțiuni și facilitând astfel procesul de depanare și punere la
punct al programului.
Iteration = iterație, (1) un proces care se repetă, (2) un singur pas al unei bucle de prelucrare.
J
JSON (JavaScript Object Notation) = un format standard open-source, pentru schimbul datelor, care
utilizează text pentru memorarea și transmiterea datelor de tip obiect constând din perechi atribut-
valoare și date de tip tablou. Servește la la înlocuirea XML în unele situații. Este derivat din
JavaScript.
K
Keyword = cuvânt cheie, care face parte din vocabularul de bază de comenzi al procesorului.
L
Language = limbaj, mulțimea șirurilor de simboluri care respectă regulile unei gramatici.
Lifetime = durata de viață a obiectelor. Dacă spațiul de memorie pentru o variabilă este alocat
dinamic, pe stivă sau în registrele calculatorului, se spune că variabila este dinamică. Dacă spațiul de
memorie este alocat static, în zona de memorie permanentă a programului, se spune că variabila este
statică. În Python, variabilele locale sunt dinamice, variabilele globale sunt statice.
Lint, linting = este procesul de analizare statică a unei secvențe de program pentru descoperirea
anticipată unor posibile erori de tip sintactic, erori de stil sau construcții suspicioase.
Liskov substitution principle (POO) = principiu referitor la obiecte introdus de Barbara Liskov în
1988, conform căruia dacă un obiect de tipul S este un subtip al obiectului de tip T, pe care îl extinde,
atunci substituirea lui T cu S nu ar trebui să perturbe funcționarea programului.
M
Magic methods = funcții de tip dunder: __init__(), etc., fiind apelabile indirect (transparent).
Memoisation = tehnică de reducere a timpului de calcul în cazul rulării unor algoritmi de tip recursiv,
prin memorizarea unor rezultate parțiale și refolosirea lor în cazul repetării execuției algoritmului.
Mapping type = un tip de dată formată dintr-o colecție de chei asociate cu valori. În Python singurul
tip de astfel de dată este dicționarul.
Markdown = limbaj de marcare (asemănător HTML), utilizat intensiv în proiectul Jupiter-lab.
Method (POO) = un tip special de funcție (metodă) definită în cadrul definiției clasei.
Mutable data = valoarea unei date mutabile poate fi modificată (liste, dicționare).
N
Namespace = spațiu al numelor.
411 Glossar
Naming collision = o situație în care două sau mai multe nume aparținând unui spațiu de nume sunt
ambigue.
nbsp (non breaking space) = cod inserat în paginile tip .html care de regulă ignoră spațiile în număr
mai mare ca 1, pentru a reprezenta spații suplimentare ( &).
Nod = un element al unei liste, sau graf, sau arbore care conține atât datele cât și un pointer la
elementul următor al structurii de date.
Non-local = în Python se pot defini funcții în funcții. Ca urmare apare domeniul non-local, în afară de
domeniile local și global. Declarația unei variabile non-locale poate apărea, de exemplu, în cadrul
unei funcții apelate de o altă funcție.Variabila de nivel inferior non-locală poate fi locală la nivel
superior.
O
Object (POO) = o instanță (obiect) unică a unei structuri de date de tip clasă. Un obiect
conține date membre (variabile clasă și variabile instanță) și metode.
Off the shelf = gata de folosință, de pe raft
(Function) Overloading (POO) = atribuirea mai multor moduri de comportare unei funcții
(supraîncărcarea funcției). Operațiile ce pot fi efectuate depind de tipul obiectelor și argumentelor
implicate.
(Operator) Overloading (POO) = atribuirea mai multor funcționalități unui anumit operator
(supraîncărcarea operatorului).
P
Parametru (formal/nume argument) = nume al unei entități generice utilizate în definiția unei funcții
(nume argument). Valoarea parametrului nu este precizată în momentul definiției.
Parametru (effectiv/actual) = entitate care particularizează entitatea generică desemnată de un
parametru formal al unei funcții în momentul apelului acesteia.
Pattern = schemă, șablon, formă
Parse, parser = analiză, analizor
R
Reference = referință, un alt nume dat unei variabile sau unui obiect. Referința nu creează alte entități
distincte în memorie față de original. Uneori, deși originalul a fost șters din memorie, acesta mai poate
fi accesat în continuare prin intermediul referinței.
Recursive call = apelul unei funcții la ea însăși
Read–Eval–Print Loop (REPL) = un mediu al unui limbaj de programare, interactiv, care primește o
intrare, o execută (evaluează) și întoarce rezultatul către utilizator. Apoi se reia ciclul cu o nouă
intrare.
S
Scientific stack = stiva științifică este formată din Python, Matplotlib, Numpy, Pandas, Scipy, Scikit-
learn și alte biblioteci. Permite procesarea și vizualizarea datelor cu caracter științific și tehnic.
Scope = domeniul de vizibilitate al numelui N, corespunzător al unei entități E, este spațiul
programului în care N desemnează entitatea E. Se referă în general la domeniul de vizibilitate al
variabilelor în interiorul și exteriorul funcțiilor.
Semicolon = punct și virgulă (;)
Sequence = orice fel de tip de dată care constă într-o colecție ordonată de elemente, fiecare element
fiind identificat de un index.
412 Glossar
Side effect = efect lateral, o schimbare a stării unui program (o modificare a valorii unei variabile,
etc.) care apare la apelul unei funcții. In general este neprevăzut, iar consecințele nedorite.
Statement = declarație, instrucțiune
Shell = coajă, carapace, scoică. În software se utilizează cu sensul de cadru, mediu de lansare a
comenzilor sau programelor, cu utilități minimale de editare în linie și posibil cu meniu foarte simplu
de dezvoltare
SOA (Service-Oriented Architecture) = se referă la o aplicație care e făcută din componente conectate
în cadrul unei rețele
Sort = un proces de organizare a unei colecții de date în ordine ascendentă, sau descendentă.
Stack = stivă, un tip abstract de date (căruia îi corespunde o zonă de memorie) în care cel mai recent
element inserat este primul element extras sau regăsit. Această proprietate se numește “ultimul intrat,
primul ieșit”, sau last-in, first-out (LIFO).
Data structure = structură de date, o construcție definită într-un limbaj de programare pentru
memorarea unei colecții de date.
Stride, strides = reprezintă numărul de octeți care trebuie săriți în memorie pentru a ajunge la
următorul element al tabloului
T
Traverse = traversare, o iterare prin elementele unei colecții cu executarea unor operații similare
asupra fiecăruia.
Tree = arbore, un graf conectat fără bucle.
Token = un element de bază în structura sintactică a unui program, similar cuvântului într-un limbaj,
termen.
Tuple = abstractizarea unei secvențe: single, double, triple, quadruple, quintuple, sextuple, septuple,
octuple, ..., n-tuple, .... În limbajele de programare, un tuplu este în general o listă de elemente între
paranteze rotunde ( ), separate prin virgulă. Un tuplu este în general nemutabil (elementele sale sunt
fixate și nu mai pot fi modificate), dar poate fi și mutabil dacă elementele și valorile lor mai pot fi
schimbate.
Type = o categorie de valori numerice sau nenumerice.
U
Ufunc = funcție utilizator, sau funcție universală.
V
Variable = variabila, în limbajele de programare este un nume simbolic asociat cu o locație din
memoria calculatorului identificată de o adresă. Locația de memorie conține o cantitate cunoscută sau
necunoscută de informație denumită valoare. Separarea numelui de conținut permite ca numele
variabilei să fie folosit independent de valoarea exactă a informației. Se mai spune că numele
variabilei este o referință (referă) valoarea stocată în locația din memorie. Numele variabilei poate fi
legat de valoarea stocată la momentul run-time și conținutul locației poate fi modificat în timpul
execuției programului (variabilă mutabilă). Dacă schimbarea este interzisă, se spune că variabila este
imutabilă, deși în acest caz denumirea de variabilă este improprie.
(Class) Variable (POO) = o variabilă care este partajată de toate instanțele clasei. Variabilele clasei
sunt definite în cadrul clasei, dar în afara oricărei metode a clasei. De regulă, variabilele clasei nu sunt
utilizate la fel de frecvent ca variabilele instanței.
413 Glossar
(Global) Variable = variabilă globală; se referă la o variabilă definită în exteriorul funcției. Dacă în
funcție se dorește modificarea variabilei, se declară variabila ca fiind „global”. Dacă se dorește doar
să se citească, nu este necesar.
(Instance) Variable (POO) = o variabilă definită în cadrul unei metode și care aparține unei instanțe
curente a clasei.
Strongly typed variable = în cazul unei variabile strongly typed (de tip tare) nu este permis ca tipul
acesteia să se schimbe prin conversii implicite. Schimbarea tipului se poate face numai prin conversii
explicite.
Virtual Environment = în Python, un mediu izolat care permite să fie instalate pachete doar pentru o
anumită aplicație.
W
Web scrapping = extragerea unor secvențe de informație din mai multe pagini web (de exemplu,
prețul unui anumit produs).
Widget (graphical widget) = este o componentă software a unei interfețe grafice, ca de exemplu un
buton, o casetă de afișare sau o bară derulantă. Mai este denumită “element grafic de control” și
permite citirea, scrierea sau editarea directă, printr-o metodă specifică, a unei informații în cadrul
unei aplicații software. Widgeturile se găsesc reunite în colecții (toolkit) sau biblioteci (library) ca:
Windows Presentation Foundation, GTK, etc. Facilitează reutilizarea componentelor software.
Wrapper = învelitoare, ambalaj.
Whitespace = orice caracter care produce mutarea cursorului fără imprimarea unui caracter vizibil.
X
XML, eXtensible Markup Language = este un format pentru un “limbaj de descriere”, care permite
reprezentarea datelor structurate cu ajutorul unor marcaje.
Eugen Diaconescu Limbajul și ecosistemul de programare Python
BIBLIOGRAFIE
https://www.tutorialsteacher.com https://data-flair.training/blogs/
https://domeniulit.com/ https://tutorialsteacher.com
https://medium.com/ https://journaldev.com
https://dotnettutorials.net https://techdecodetutorials.com/
https://w3schools.com https://zetcode.com
https://tutorialspoint.com https://itvoyagers.in/
https://udemy.com https://w3resource.com/
https://www.datacamp.com/ https://www.afterhoursprogramming.com/
https://www.coursera.org https://riptutorial.com/
https://www.sololearn.com/learning https://jobtensor.com/
https://www.techbeamers.com/ https://stackabuse.com/
https://simplivlearning.com/ https://people.duke.edu/
https://www.edx.org https://www.edureka.co/
https://hackr.io/ https://coderslegacy.com
https://bitdegree.org https://note.nkmk.me/
https://www.codecademy.com/
https://www.afterhoursprogramming.com/
https://open.edx.org (fondat de Harvard și MIT
în 2012)
https://python.org https://askpython.com
https://realpython.com/ https://python.hotexamples.com/
https://docs.scipy.org https://pillow.readthedocs.io/
https://docs.spyder-ide.org/ https://scikit-image.org/
https://learnPython.org https://pynative.com/
http://inventwithpython.com/ https://holypython.com/
https://pythonspot.com/ https://www.practicepython.org/
https://pythonexamples.org https://seaborn.pydata.org/
https://pythonguides.com/ https://problemsolvingwithpython.com/
https://www.pythonistacafe.com/ https://pythontic.com/
https://tutorial.djangogirls.org/
https://pythonprogramming.net
https://python-course.eu
415 Bibliografie
Cărți
1. Vlad Tudor, Curs de programare în Python 3 – Fundamente pentru începători, vol. 1, Ed. L&S Soft,
2020, 215 pag.
2. Doru Anastasiu Popescu, Python 3 – Noțiuni Fundamentale, Culegere de Probleme, Ed. L&S Soft,
2020, 222 pag.
3. Swaroop C H, A Byte of Python, free book, Self-publishing, 2013
4. Allen B Downey, Think Python, How to think like a computer scientist, Green Tea Press, 2ed, 2012
5. Eric Matthes, Python Crash Course: A Hands-On, Project-Based Introduction to Programming, No
Starch Press, 2016, 2ed
6. Zed A. Shaw, Learn Python 3 the Hard Way,
7. Programming Language Academy, Python Workbook – Learn how to quickly and effectively program
with exercises, projects, and solutions, 2010
8. Paul Barry, Head First Python, O’Reilly Media, Inc., 2nd edition, 2011
9. Mark Pilgrim, Dive Into Python 3, Apress, 2009-2011
10. Michael Driscoll, Python 101, 2nd Edition, Leanpub, 2020, 760pag
11. Dan Bader, Python Tricks: The Book , 2017, 299pag
12. Luciano Ramalho, Fluent Python, O’Reilly, 2015, 766 pag
13. Tarek Ziadé and Michal Jaworski, Expert Python Programming, Packt Publishing, 2008, 372 pag
14.Doug Hellmann, The Python 3 Standard Library by Example, Pearson Education, 2017, 1454 pag
15.Peter Wentworth, Jeffrey Elkner, Allen B. Downey and Chris Meyers, How to Think Like a
Computer Scientist: Learning with Python 3 Documentation, 3rd Edition, 2019, 367 pag
16.Mark Lutz, Learning Python, Fifth Edition, O’Reilly, 2013, 1594 pag
17.Paul Deitel, Harvey Deitel, Python for Programmers, Pearson, 2019, 810pag
18.David Beazley, Brian K. Jones, Python Cookbook, O’Reilly, 2015, 706 pag
19.Dusty Philips, Python 3 Object-Oriented Programming, Third Edition, Packt, 2018, 456 pag
20.Nigel George, Build a Website with Django 3, GNW Independent Publishing, 2019
21.John E. Grayson, Python and Tkinter Programming, Manning, 2000
22.John Hunter, Darren Dale, Eric Firing, Michael Droettboom, Matplotlib, Release 3.1.1, 2019, 2354
pag
23.Sandro Tosi, Matplotlib for Python Developers, Packt Publishing, 2009, 307 pag
24.Ivan Idris, NumPy Beginner’s Guide, Packt Publishing, 2013, 310 pag
25.Numpy Community, NumPy User Guide, Release 1.17, 2019, 156 pag
26.Matt Harrison, Learning the Pandas Library, Treading on Python Series, 2016, 208 pag
27.Stack Overflow contributors, Learning Pandas, Free eBook, https://riptutorial.com/ebook/pandas,
172 pag
28.Sandipan Dey, Hands-ON Image Processing with Python, Packt Publishing, 2018, 155 pag
29.Jason M. Kinser, Image Operators – Image processing in Python, CRC Press, 2019, 366 pag
30.R. Chityala, S. Pudipeddi, Image Processing and Acquisition using Python, Second Edition, CRC
Press, 2021, 453 pag
31.Gaël Varoquaux, Emmanuelle Gouillart, Olav Vahtras, Valentin Haenel, Nicolas P. Rougier, et al.,
Scipy Lecture Notes: One document to learn numerics, science, and data with Python. Zenodo,
2015, 368 pag
32.SciPy Community, SciPy Reference Guide, Release 0.16.0, 2015, 1603 pag