Documente Academic
Documente Profesional
Documente Cultură
Capitolele anterioare Introducere și Sintaxă , Operații de bază și Programare Orientată Obiect prezintă noțiunile de concept general specifice unui limbaj modern. Însă Python are câteva particularități care îl transformă într-un limbaj special. Vom aborda o
parte din ele în cele ce urmează.
List comprehensions
List comprehensions este un mecanism simplu, intuitiv și concis de a crea liste. Pentru acesta, se folosesc o pereche de paranteze pătrate care conțin o expresie și o buclă for, urmată sau nu de zero sau mai multe condiționale. O listă nouă va fi mereu
rezultatul evaluării expresiei în contextul buclei și a conditionalelor.
[0, 2, 4]
if conditionala:
expresie
>>> patrate_perfecte = []
for x in range(10):
patrate_perfecte.append(x**2)
[9,12,15]
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48]
Un aspect important de luat în seamă este faptul că expresia inițială, lista în care se iterează sau condiționala pot lua forme mai avansate. Astfel, mecanismul de list comprehensions poate conține expresii mai complexe și funcții îmbricate.
Exemplu - afișarea numerelor care se divid și cu 3 și cu 5 dar care sunt mai mici decat 50:
http://docs.python.org/2/tutorial/datastructures.html#list-comprehensions [http://docs.python.org/2/tutorial/datastructures.html#list-comprehensions]
http://www.dalkescientific.com/writings/NBN/list_comps.html [http://www.dalkescientific.com/writings/NBN/list_comps.html]
http://effbot.org/zone/python-list.htm [http://effbot.org/zone/python-list.htm]
Din raționament teoretic, să luam ca exemplu următoarea problemă: dându-se o matrice, să se obțină transpusa ei. Așadar, presupunem că am avea o matrice 5×4 implementată ca o listă de 5 liste cu dimensiunea 4:
>>> matrice = [
... ]
>>> matrice
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19, 20]]
>>> transpusa = []
... linie_transpusa = []
... linie_transpusa.append(linie[i])
... transpusa.append(linie_transpusa)
...
>>> transpusa
[[1, 5, 9, 13, 17], [2, 6, 10, 14, 18], [3, 7, 11, 15, 19], [4, 8, 12, 16, 20]]
>>> transpusa = []
...
>>> transpusa
[[1, 5, 9, 13, 17], [2, 6, 10, 14, 18], [3, 7, 11, 15, 19], [4, 8, 12, 16, 20]]
>>> transpusa
[[1, 5, 9, 13, 17], [2, 6, 10, 14, 18], [3, 7, 11, 15, 19], [4, 8, 12, 16, 20]]
Prin urmare, avantajele folosirii mecanismului de nested list comprehension sunt evidente. Cu toate acestea, în practică, nu întotdeauna folosirea acestui mecanism este cea mai lizibilă soluție. Pentru a rezolva aceasta problema, limbajul Python pune la
dispoziție și alte metode pe care le vom aborda însă în capitolul următor.
Dictionary comprehensions
Începând cu versiunea 2.7+ a limbajului Python, un mecanism similar celui de list comprehension a fost adăugat și pentru dicționare (e.g.dictionary comprehension). Similar celui de liste, acest mecanism creează un nou dicționar; prin urmare, acest
mecanism nu poate fi folosit pentru a adăuga chei într-un dicționar deja existent. Așadar, contextul de perechi cheie-valoare trebuie precizat la construcție.
Exemplu - setarea tuturor cheilor la valoarea booleana True pentru primele 10 numerele naturale:
{0: True, 1: True, 2: True, 3: True, 4: True, 5: True, 6: True, 7: True, 8: True, 9: True}
http://infohost.nmt.edu/tcc/help/pubs/python/web/dict-comprehensions.html [http://infohost.nmt.edu/tcc/help/pubs/python/web/dict-comprehensions.html]
http://www.python.org/dev/peps/pep-0274/ [http://www.python.org/dev/peps/pep-0274/]
Map/Lambda/Filter
map() [http://docs.python.org/2/library/functions.html#map], lambda [http://www.u.arizona.edu/~erdmann/mse350/topics/list_comprehensions.html#lambda] și filter() [http://docs.python.org/2/library/functions.html#filter], alături de mecanismul de list
comprehensions furnizează un mod compact, elegant, și eficient de a programa.
Map
Este o construcție builtin în Python care aplică o funcție fiecărui membru dintr-o colecție iterabilă, oricare ar fi ea (listă, tuplu, etc) și întoarce rezultatul.
Exemplu - afișarea primelor zece pătrate perfecte (rescrierea exemplului de la list comprehensions):
return x**2
>>> patrate_perfecte = map(patrat, range(10))
[5, 6, 5, 5, 3, 7]
Lambda
În primul exemplu de mai sus din cadrul construcției map am creat o funcție numita patrat pentru a putea fi aplicată în context peste secvența dată. Aceasta este o situație des întâlnită în Python. Ca urmare, limbajul pune la dispoziția programatorului o
metodă mai flexibilă și mai robustă de a crea funcții simple anonime (fară declarații suplimentare în blocul de execuție) cu ajutorul așa numitei funcții lambda. Așadar, o funcție simplă, anonimă care returnează rădăcina pătrată a argumentului transmis poate fi
rescrisă astfel: lambda x: x ** 2. În traducere liberă expresia înseamnă: O funcție anonimă (fără nume) care primește un argument x și returnează rădăcina pătrată a acestuia.
>>> print (lambda x: x**2)(5) # primul set de paranteze definesc funcția lambda, al doilea set de paranteze apelează funcția
25
>>> # creează o funcție cu două argumente (x și y) care returnează produsul lor, apoi apelează funcția cu argumentele 3 și 4
12
False
Filter
Este de asemenea o construcție builtin în Python care primește ca argument o funcție care întoarce True sau False și o aplică pe o secvență, întorcând ca rezultat o listă care conține doar acele elemente din secvență pentru care funcția aplicată întoarce True.
Exemplu - putem rescrie o parte din exemplele de mai sus, mai succint, astfel încât sa obținem pătratele perfecte între mai mari decat 10 dar mai mici decat 50:
http://www.u.arizona.edu/~erdmann/mse350/topics/list_comprehensions.html#map-lambda-and-filter [http://www.u.arizona.edu/~erdmann/mse350/topics/list_comprehensions.html#map-lambda-and-filter]
http://www.diveintopython.net/power_of_introspection/filtering_lists.html [http://www.diveintopython.net/power_of_introspection/filtering_lists.html]
http://www.diveintopython.net/power_of_introspection/lambda_functions.html [http://www.diveintopython.net/power_of_introspection/lambda_functions.html]
http://www.diveintopython.net/native_data_types/mapping_lists.html [http://www.diveintopython.net/native_data_types/mapping_lists.html]
dir()
În capitolul de Operații de bază, spuneam că limbajul Python are un set de funcții foarte folositoare pe care le pune la dispoziție în mod direct (e.g. string(), type(), funcții de conversie, funcții matematice, etc). Din aceeași categorie, o altă funcție importantă
este funcția dir().
Extrem de utilă în cazuri simple dar mai ales în contextul programării orientate-obiect, funcția dir() întoarce o listă de atribute și metode ale unui obiect, oricare ar fi acesta: modul, funcție, string, listă, dicțtionar, etc.
>>> dir(li)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__
>>> d = {}
>>> dir(d)
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__
>>> dir(string)
['Formatter', 'Template', '_TemplateMetaclass', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_float', '_idmap', '_idmapL', '_int', '_long', '_multimap', '_re', 'ascii_letters', 'ascii_lowercase', 'ascii_u
Observații:
li este o listă, așadar dir(li) returnează o listă cu toate metodele unei liste. Lista întoarsă conține numele metodelor ca stringuri
string în ultimul exemplu este un modul, așa încât dir(string) întoarce o listă cu mai multe tipuri de rezultate (e.g. atribute built-in ca __name__, __doc__, și/sau alte atribute definite de programator)
__new__(cls, […)
__new__ este prima metoda apelată în momentul instanțierii unui obiect. Ea primește ca parametrii tipul clasei urmată de orice alt argument pe care le va transmite mai departe metodei __init__. __new__ este folosită destul de rar, însă are
particularitățile ei specifice.
__init__(self, […)
Metoda de inițializare a clasei. Primește ca argument orice parametru a fost transmis în momentul apelării constructorului (de exemplu, daca am fi apelat x = SomeClass(10, 'foo'), __init__ ar fi primit ca argumente 10 și foo. __init__ este
folosită universal in definirea claselor în Python.
__del__(self)
Dacă __new__ și __init__ formează împreună constructorul clasei, __del__ este destructorul. Atentie! El nu implementează comportamentul instructiunii del x (deci codul nu se traduce prin x.del()!!). Mai degrabă acesta completează
comportamentul obiectului în momentul în care este eliberata memoria (util de exemplu în situația sockets-ilor sau a fișierelor obiect)
class FisierObiect:
'''Clasa wrapper la fisierele obiect pentru a asigura inchiderea handler-ului de citire inainte ca fisierul sa fie sters.'''
def __del__(self):
self.file.close()
del self.file
Dintre aceste metode “magice”, vom exemplifica patru dintre ele, care au o importanță majoră: __getitem__, __call__, __getattr__, __setattr__.
__getitem__
Implementarea metodei __getitem__ într-o clasă permite instanțelor sale să folosească operatorul de indexare [].
class Test(object):
t = Test()
t[1]
t['salut studenti!']
t[5:200:10]
t['a':'z': 3]
t[object()]
<type 'int'> 1
Observații:
Spre deosebire de alte limbaje, Python permite transmiterea oricarui tip de obiect prin operatorul de indexare []. Astfel, expresia t[1, 'b', 3.0] este parsată fără probleme de interpretor. În mecanismul intern, aceasta instrucțiune se traduce prin
t.__getitem__((1, 'b', 3.0)) (tratată ca un tuplu)
__getitem__ este de cele mai multe ori folosită pentru indexarea listelor, căutare in dicționare sau accesarea unor intervale de valori
__call__
Implementarea metodei __call__ într-o clasă permite instanțelor sale să devină apelabile – cu alte cuvinte, acele instanțe se comportă ca niște funcții. Pentru a testa dacă un anumit obiect este sau nu apelabil se poate folosi metoda built-in callable (returnează
True pentru funcții, metode și obiecte care au __call__
class Test(object):
print kwargs
print '-'*30
t = Test()
t(1, 2, 3)
(1, 2, 3)
{}
------------------------------
()
------------------------------
(4, 5, 6)
------------------------------
Observații:
Metoda “magică” __call__ se implementează ca orice altă metodă sau funcție. Singura diferență este ca pentru a o apela, nu e nevoie de un nume ci doar de paranteze rotunde ().
__getattr__
Implementarea metodei __getattr__ într-o clasă permite instanțelor sale să suprascrie mecanismul de acces la membri.
class Test(object):
def __init__(self):
self.a = 'a'
self.b = 'b'
t = Test()
print t.a
print t.b
print t.c
123456
123456
True
Observații:
După cum se poate observa din exemplul de mai sus, metoda __getattr__ este invocată doar pentru atribute care nu se regăsesc în atributul “magic” __dict__. Implementarea __getattr__ face ca metoda built-in hasattr() sa întoarcă mereu True, cu
excepția cazului în care o excepție este aruncată în mod expres în blocul de instrucțiuni al metodei __getattr__.
__setattr__
Deși implementarea metodei __setattr__ nu este foarte întâlnită, am considerat că este bine să fie amintită pentru o privire de ansamblu. Implementarea ei într-o clasă permite instanțelor sale să suprascrie mecanismul de atribuire al membrilor.
class Test(object):
def __init__(self):
self.a = 'a'
self.b = 'b'
t = Test()
t.c = 'z'
setat a la 'a'
setat b la 'b'
setat c la 'z'
setat d la '888'
Observații:
Surprinzător sau nu, metoda __setattr__ nu este simetrică cu __getattr__!! În particular, toate atribuirile trec prin __setattr__, chiar și pentru variabilele prezente in atributul “magic” __dict__ . Din acest motiv, implementarea lui __setattr__ nu
este recomandată.
http://www.rafekettler.com/magicmethods.html#intro [http://www.rafekettler.com/magicmethods.html#intro]
http://www.ironpythoninaction.com/magic-methods.html [http://www.ironpythoninaction.com/magic-methods.html]
http://docs.python.org/2/reference/datamodel.html#basic-customization [http://docs.python.org/2/reference/datamodel.html#basic-customization]
Exercitii
Observație: Folosiți cât mai mult mecanismele de list comprehension și/sau map/filter/lambda.
1. Scrieți o funcție care primește ca argument o listă L de cuvinte și întoarce ca rezultat o altă listă care conține prima litera din fiecare cuvânt al listei inițiale.
Soluție
>>> l = ['Ana', 'are', 'mere', 'iar', 'Costel', 'are', 'pere']
2. Scrieți o funcție care primește ca argument o listă L de cuvinte și întoarce ca rezultat o altă listă care conține aceleași cuvinte din lista inițială dar scrise cu majuscule.
Soluție
>>> l = ['ana', 'are', 'mere', 'iar', 'costel', 'are', 'pere']
>>> print l2
['ANA', 'ARE', 'MERE', 'IAR', 'COSTEL', 'ARE', 'PERE']
3. Scrieți o funcție care primește ca argument o listă L și întoarce ca rezultat o altă listă care conține toate elementele din L duplicate.
Soluție
>>> def dupli(L):
[1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
4. Scrieți o funcție care primește ca argument o listă L și un număr întreg N, și întoarce ca rezultat o altă listă care conține toate elementele din L duplicate de N ori.
Soluție
>>> def dupli(L, N):
... return [x for x in L for i in range(N)]
...
>>> dupli([1, 2, 3], 5)
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3]
5. Scrieți o funcție, care primește ca argument o listă L și un număr întreg N, care șterge fiecare al N-lea element din listă.
Soluție
>>> def drop(L, N):
...
>>> drop([1, 2, 3, 1, 2, 24, 3, 3, 2, 234, 1234], 3)
[1, 2, 1, 2, 3, 3, 234, 1234]
6. Scrieți o funcție care primește ca argument două liste L1 și L2 și întoarce ca rezultat o listă conținând toate elementele care se află în L1 dar nu în L2.
Soluție
>>> def separate(L1, L2):
...
>>> separate([1, 2, 3], [4, 5, 6])
[1, 2, 3]
[1, 2, 3]
[3]
7. Dându-se o listă L de cuvinte și o altă listă suf de sufixe, scrieți o funcție care întoarce cuvintele din L cu sufixul eliminat dacă acesta se regăsește în suf. Observație: dacă un cuvânt conține mai mult de un sufix din suf, se elimină cel mai lung dintre ele.
Soluție
>>> def token_stemming(L1, suf):
result = []
found_suffix = False
if word.endswith(suffix):
result.append(word[:-len(suffix)])
found_suffix = True
break
if not found_suffix:
result.append(word)
return result
Soluție
>>> def odd(*args):
9. Dându-se o listă L de cuvinte și un număr întreg N, scrieți o funcție care întoarce cele mai frecvente N cuvinte din listă.
Soluție
>>> def count_words(L, N):
freq = {}
for word in L:
if word in freq:
freq[word] += 1
else:
freq[word] = 1
sorted_frequencies.reverse()
>>> count_words(['Gigel', 'si', 'Costel', 'si', 'iar', 'Costel', 'fug', 'de', 'celalalt', 'Gigel'], 3)
10. Scrieți o funcție care afișează suma primelor 1000000 de numere naturale folosind generatori.
Soluție
>>> def firstn(n):
num = 0
yield num
num += 1
499999500000