Spotkania z Pythonem - Czesc 3 - wyrazenia lambda

Transkrypt

Spotkania z Pythonem - Czesc 3 - wyrazenia lambda
Spotkania z Pythonem
Cz¦±¢ 3 - wyra»enia lambda, generatory, dekoratory, operacje na plikach
Michaª Alichniewicz
Studenckie Koªo Automatyków SKALP
Gda«sk 2014
Dzi¦kuj¦ za uwag¦!
Na licencji Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License.
M. Alichniewicz (SKALP)
Python vol. 3
2014
1 / 66
Wyra»enia lambda
M. Alichniewicz (SKALP)
Python vol. 3
2014
2 / 66
Wyra»enia lambda - co to jest?
Zacznijmy od tego, czym jest wyra»enie lambda. Z denicji, jest to
anonimowa funkcja (czyli - nie ma nazwy; ale jest równie» obiektem, wi¦c
mo»emy j¡ sobie gdzie± przypisa¢), która przyjmuje zadane argumenty, a
nast¦pnie u»ywa ich w tzw. wyra»eniu. Wynik wyra»enia jest warto±ci¡
która taka funkcja zwraca.
Najprostsz¡ analogi¡ która mo»e uªatwi¢ zrozumienie funkcji lambda jest
zwyczajna
funkcja matematyczna.
Przyjmuje ona jakie± argumenty, które
nast¦pnie s¡ cz¦±ci¡ skªadow¡ jakiego± matematycznego wyra»enia, dla
przykªadu
f (x) = 5x2 + x + 0.5 −
M. Alichniewicz (SKALP)
Python vol. 3
1
x
2014
3 / 66
Wyra»enia lambda - co to jest?
Dlaczego akurat analogia do matematyki? Otó», w takiej prostej funkcji
nie wyst¦puje logika, caªa funkcja jest tylko i wyª¡cznie pojedy«czym
wyra»eniem. Druga cecha która powoduje »e funkcja matematyczna jest
dobra do przedstawienia jako analogia funkcji lambda jest fakt, »e w
matematyce wszystkie zmienne i nazwy funkcji s¡ arbitralne. Czyli mo»emy
mie¢ funkcj¦
f (x),
ale równie dobrze mo»e to by¢
alfabetu. To, co jest tu niezmienne, to
M. Alichniewicz (SKALP)
g(a),
wyra»enie.
Python vol. 3
czy inne litery
2014
4 / 66
Wyra»enia lambda - co to jest?
Zaªó»my, »e chcieli by±my zdeniowa¢ w Pythonie tak¡ funkcj¦ jak ta
opisana wzorem z poprzednich slajdów. W klasyczny sposób, by to
wygl¡daªo tak:
def nasza_funkcja(x):
# Oblicz i zwroc
return 5*x**2 + x + 0.5 - 1/x
Jak widzicie, nie ma tutaj
ciaªa funkcji - wszystko co funkcja robi to
zwraca warto±¢.
M. Alichniewicz (SKALP)
Python vol. 3
2014
5 / 66
Wyra»enia lambda - denicja
Poniewa» ciaªa funkcji jako takiej nie ma, to mo»na to skróci¢ do
wspomnianego wyra»enia lambda.
Wyra»enia lambda deniuje si¦ nast¦puj¡co:
lambda arg1, arg2, ...: wyrazenie
arg1, arg2, etc.
to argumenty funkcji lambda, za± wyra»enie jest
naszym. . . wyra»eniem, którego wynik jest warto±ci¡ jakie nasza funkcja
lambda zwraca. Oczywi±cie funkcje lambda tak jak tradycyjne funkcje w
Pythonie, mog¡ przyjmowa¢ domy±lne warto±ci argumentów, mo»emy
podawa¢ argumenty przez nazw¦ przy wywoªywaniu funkcji, tak samo jak
mo»emy zdeniowa¢ zmienn¡ liczb¦ argumentów.
M. Alichniewicz (SKALP)
Python vol. 3
2014
6 / 66
Wyra»enia lambda - denicja
Poniewa» sama funkcja lambda nie ma identykatora, trzeba j¡ gdzie±
przypisa¢:
f = lambda arg1, arg2, ...: wyrazenie
Skoro i tak trzeba t¡ funkcj¦ gdzie± przypisa¢, to po co jej u»ywa¢? Czy ma
to sens, skoro mo»emy sobie tak¡ funkcj¦ zdeniowa¢ tradycyjnie? A
no. . . nie ma.
Zastosowanie funkcji lambda ma sens dopiero z innymi funkcjami, jak dla
przykªadu
reduce, lter oraz map.
Wszystkie wymienione funkcje
przetwarzaj¡ w pewien sposób list¦ podan¡ przez u»ytkownika. A w jaki
sposób? Równie» w pewien sposób zdeniowany przez u»ytkownika - jako
pierwszy argument przyjmuj¡ funkcje która operuje na elementach. Funkcje
mo»emy albo zdeniowa¢ normalnie - co poniek¡d za±mieca kod, bo mamy
jak¡± dziwn¡ funkcj¦ nie wiadomo po co, albo wªa±nie u»y¢ wyra»enia
lambda.
M. Alichniewicz (SKALP)
Python vol. 3
2014
7 / 66
Wyra»enia lambda - funkcja reduce
Funkcja reduce przyjmuje dwa argumenty - drugi to lista, z kolei pierwszy
jest funkcj¡ dwóch parametrów. Jej dziaªanie opiera si¦ na zredukowaniu
listy do pojedynczego wyniku w sposób zdeniowany przez u»ytkownika, na
zasadzie:
y = f (f (f (f (f (x1 , x2 ) , x3 ) , x4 ) , x5 ) . . .)
Co mo»na z tym zrobi¢? Np. zsumowa¢ argumenty listy (mo»na to
zrobi¢ normaln¡ p¦tl¡ for, ale po co?). W takim wypadku, nasza funkcja to
po prostu:
f (x, y) = x + y
M. Alichniewicz (SKALP)
Python vol. 3
2014
8 / 66
Wyra»enia lambda - funkcja reduce
A jak to wygl¡da w praktyce? Nasz¡ funkcj¦ redukuj¡c¡ mo»emy
zdeniowa¢ jako wyra»enie lambda:
lambda x,y: x + y
Nie musimy jej nigdzie przypisywa¢ - mo»emy je poda¢ wprost jako
argument funkcji. Mamy wtedy:
reduce(lambda x, y: x+y, nasza_lista)
Sprawd¹my wi¦c dziaªanie:
# Zwroci 21
reduce(lambda x, y: x + y, [1,2,3,4,5,6])
M. Alichniewicz (SKALP)
Python vol. 3
2014
9 / 66
Wyra»enia lambda - funkcja reduce
Korzystaj¡c z funkcji reduce, mo»emy bardzo ªatwo policzy¢ silni¦ - jak
pami¦tamy z matematyki, to iloczyn kolejnych liczb od 1 do docelowej.
W takim przypadku, funkcja lambda b¦dzie wygl¡da¢ nast¦puj¡co:
lambda x,y: x * y # Mnoymy ssiednie elementy
W poª¡czeniu z funkcj¡
range, która deniuje nam list¦, mo»emy
zdeniowa¢ ko«cow¡ funkcj¦ n!:
reduce(lambda x, y: x * y, range(1,n))
Sprawd¹my wi¦c dziaªanie:
# Zwroci 120
reduce(lambda x, y: x * y, range(1,6))
M. Alichniewicz (SKALP)
Python vol. 3
2014
10 / 66
Wyra»enia lambda - funkcja lter
Drug¡ funkcj¡ z kolekcji jest
lter.
Ta funkcja dla odmiany zwraca list¦,
która skªada si¦ z elementów listy któr¡ podajemy jako drugi argument, dla
których
wynik funkcji podanej w pierwszym argumencie jest prawd¡.
Do czego to mo»na wykorzysta¢? A no na przykªad do stworzenia listy
kolejnych liczb parzystych. Jak to zrobi¢? Zaªó»my, »e nasza funkcja to to
prostu dzielenie modulo 2. Dla liczb parzystych - otrzymamy 1; dla liczb
nieparzystych, otrzymamy 0, co jest interpretowane jako faªsz. Neguj¡c
wynik otrzymamy prawd¦ dla liczby parzystej i faªsz dla liczby nieparzystej.
Oczywi±cie najlepiej to zobrazuje przykªad:
# Zwroci [2,4,6,8]
filter(lambda x: not x % 2, range(1,10))
M. Alichniewicz (SKALP)
Python vol. 3
2014
11 / 66
Wyra»enia lambda - funkcja map
Ostatnia funkcja z kolekcji to
map.
Tak jak poprzednia zwraca list¦, tylko
»e dla odmiany tutaj dostajemy wynik w postaci tych samych elementów,
ale po przetworzeniu przez podan¡ przez nas funkcj¦ (ilo±¢ jest ta sama, nic
nam nie ubywa). Funkcja jest wywoªywana dla ka»dego elementu
oddzielnie. Za pomoc¡ tego mo»emy np. podnie±¢ liczby z listy do
kwadratu lub np. zamieni¢ wszystkie teksty z listy na pisane wielkimi
literami.
Przykªad:
# Zwroci ['A', 'B', 'C', 'D', 'SKALP']
map(lambda x: x.upper(), ['a', 'b', 'c', 'd', 'Skalp'])
M. Alichniewicz (SKALP)
Python vol. 3
2014
12 / 66
Skrócone instrukcje
warunkowe
M. Alichniewicz (SKALP)
Python vol. 3
2014
13 / 66
Skrócone instrukcje warunkowe
Rozwa»my dwa zdania:
Je»eli warunek jest speªniony, zwró¢ A, w przeciwnym razie zwró¢ B
oraz
Zwró¢ A je»eli warunek jest speªniony, w przeciwnym razie zwró¢ B
Oba zdania maj¡ oczywi±cie logiczny sens.
M. Alichniewicz (SKALP)
Python vol. 3
2014
14 / 66
Skrócone instrukcje warunkowe
Oba zdania reprezentuj¡ opisowo skrócon¡ instrukcj¦ warunkow¡, przy
czym pierwsza jest dosªownym tªumaczeniem instrukcji znanej z C, a druga
jest jej odpowiednikiem w Pythonie.
Dla przypomnienia, w C to wygl¡da tak:
warunek ? wartosc_1 : wartosc_2
W Pythonie, to ma nieco inn¡ posta¢:
wartosc_1 if warunek else wartosc_2
Pierwsza ró»nica - zamiast nie do ko«ca czytelnych znaków ? oraz :,
mamy czytelne if i else. Druga to nieco inna konstrukcja warunku najpierw podajemy warto±¢ któr¡ zwracamy je»eli warunek jest speªniony,
pó¹niej instrukcj¦ warunkow¡ a dopiero na ko«cu warto±¢ dla faªszu.
M. Alichniewicz (SKALP)
Python vol. 3
2014
15 / 66
Generatory
M. Alichniewicz (SKALP)
Python vol. 3
2014
16 / 66
Generatory
W Pythonie istnieje mo»liwo±¢ zdeniowania funkcji, b¡d¹ klasy, po której
mo»emy
iterowa¢ - czyli wykorzysta¢ je jako ¹ródªo danych dla p¦tli for.
xrange.
W Pythonie jest jedna taka funkcja:
Je»eli sprawdzimy, co nam zwróci ta funkcja bezpo±rednio:
print xrange(0,10)
To co najwy»ej wy±wietli si¦ nam
xrange(10).
Z kolei je»eli umie±cimy t¡
instrukcj¦ w p¦tli:
for item in xrange(0,10):
print item,
To p¦tla wy±wietli nam 0 1 2 3 4 5 6 7 8 9. Tak samo zadziaªa jakby±my
podali zamiast
xrange podali range.
Z tym »e je»eli wy±wietlimy co
zwraca druga funkcja, to dostaniemy list¦. Jaka jest ró»nica?
M. Alichniewicz (SKALP)
Python vol. 3
2014
17 / 66
Generatory
Stwórzmy odpowiednik funkcji
range we wªasnym zakresie:
def my_range(start, end, step = 1):
return_ = []
while start < end:
# Wpisz akutalna wartosc na liste
# Metoda ``append'' na liscie pozwala
# na dopisanie elementu na koniec listy
return_.append(start)
start += step
return return_
Dziaªanie tej funkcji jest dokªadnie takie jak dziaªanie funkcji
range.
Tworzymy list¦ z kolejnymi elementami z zakresu.
M. Alichniewicz (SKALP)
Python vol. 3
2014
18 / 66
Generatory
W celu uªatwienia zrozumienia tego jak dziaªa generator, zróbmy sobie
odpowiednik p¦tli
for:
for item in set_of_data:
# Zrob cos z ``item''
# TUTAJ KOD DO WYKONANIA W PETLI
pass
Za pomoc¡ p¦tli
while.
M. Alichniewicz (SKALP)
Python vol. 3
2014
19 / 66
Generatory
Jedna z mo»liwych wariacji tego kodu, która bardzo dobrze oddaje
dziaªanie p¦tli
for:
# Pobierz obiekt iteratora z naszego
# obiektu po jakim chcemy wykonywac petle
iterator = set_of_data.__iter__() # (1)
# Zacznijmy petle
while True:
try:
# Wez kolejny element
# z iteratora
item = iterator.next() # (2)
# Zrob cos z ``item''
# TUTAJ KOD DO WYKONANIA W PETLI
except StopIteration: # (3)
break
M. Alichniewicz (SKALP)
Python vol. 3
2014
20 / 66
Generatory
Jak ten kod dziaªa? Przede wszystkim trzeba zwróci¢ uwag¦ »e nie
u»ywamy tutaj indeksowania elementów. Ka»dy obiekt który mo»emy
poda¢ do p¦tli
for, musi mie¢ zaimplementowan¡ metod¦ __iter__.
Zwraca ona obiekt, po którym bezpo±rednio mo»na iterowa¢ (1) - a
wymogiem takiego iterowalnego obiektu jest posiadanie zaimplementowanej
metody
next, która zwraca kolejny element z zestawu danych (2), albo
StopIteration w przeciwnym wypadku (3). Oczywi±cie
rzuca wyj¡tkim
obiekt który podajemy na wej±cie p¦tli i obiekt który jest zwrócony przez
jego metod¦
__iter__ mo»e by¢ tym samym obiektem - wymagane jest
jednak posiadanie odpowiednich metod.
M. Alichniewicz (SKALP)
Python vol. 3
2014
21 / 66
Generatory
Stwórzmy sobie tak¡ przykªadow¡ klas¦:
class my_range_class(object):
def __init__(self, start, end, step = 1):
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self
def next(self):
if self.start < self.end:
return_value = self.start
self.start += self.step
return return_value
else:
raise StopIteration()
M. Alichniewicz (SKALP)
Python vol. 3
2014
22 / 66
Generatory
Jak wida¢, w tym kodzie nigdzie nie tworzymy listy elementów! Zwracamy
je na bie»¡co, je»eli p¦tla (albo my) - o to poprosi poprzez wywoªanie
metody
next.
Ma to t¦ zalet¦, »e przede wszystkim nie generujemy dªugiej
listy w pami¦ci. Je»eli generujemy liczby, to problemu nie ma - no chyba »e
s¡ to bardzo dªugie liczby - ale dla innych obiektów mo»e si¦ okaza¢ »e
nasz program zu»ycie pami¦ci ma niczym Firefox w czasach ±wietno±ci (z
caªym szacunkiem dla przegl¡darki Mozilli). Powy»szy kod bezproblemowo
zadziaªa w bloku
for, i jego dziaªanie b¦dzie zgodne z oczekiwaniami.
M. Alichniewicz (SKALP)
Python vol. 3
2014
23 / 66
Generatory
Jak ju» si¦ przekonali±my wielokrotnie, du»o dªugich instrukcji ma w
Pythonie swoje krótsze odpowiedniki. Tak te» jest w przypadku
generatorów - zamiast peªnej klasy mo»emy zdeniowa¢ po prostu
odpowiedni¡ funkcj¦. Pisz¡c generator w postaci funkcji, informujemy
Pythona »e wªa±nie zwracamy do p¦tli jak¡± warto±¢ (ale nie ko«czymy
funkcji!) za pomoc¡ sªowa kluczowego
yield (jest to odpowiednik return:
w tym momencie wychodzimy z funkcji, ale funkcja b¦dzie wznowiona od
tego miejsca w przypadku pro±by o kolejny argument).
M. Alichniewicz (SKALP)
Python vol. 3
2014
24 / 66
Generatory
Najlepiej to zobrazuje fragment kodu (funkcje
print sªu»¡ do pokazania
dziaªania kodu):
def my_range_generator(start, end, step = 1):
print 'Wejscie do funkcji'
while start < end:
print 'Zwrocenie wartosci %d' % start
# "Wyrzuc" nowa wartosc
yield start
print 'Wznowienie funkcji'
start += step
print 'Wyjscie z funkcji'
Przeanalizujmy dziaªanie tego w praktyce.
M. Alichniewicz (SKALP)
Python vol. 3
2014
25 / 66
Generatory
Zacznijmy od utworzenia obiektu i sprawdzenia co dostali±my:
x = my_range_generator(0, 10, 2)
print repr(x)
# Dostaniemy na ekranie cos podobnego do
# <generator object my_range_generator at ...>
for musi
__iter__ - sprawd¹my wi¦c czy nasza
Jak ju» wiemy, ka»dy obiekt po którym mo»emy chodzi¢ p¦tl¡
mie¢ zaimplementowan¡ metod¦
funkcja tak¡ posiada, i co nam zwróci:
x_iter = x.__iter__()
print repr(x_iter)
# Dostaniemy na ekranie cos podobnego do
# <generator object my_range_generator at ...>
Mo»emy zauwa»y¢, »e oba polecenia
repr zwróciªy nam to samo - czyli to
jest taka sama sytuacja jak dla naszej klasy zdeniowanej wcze±niej.
M. Alichniewicz (SKALP)
Python vol. 3
2014
26 / 66
Generatory
Skoro ju» wiemy »e nasze
x jest iteratorem, mo»emy go poprosi¢ o pierwszy
element:
item = x.next()
#
#
#
#
#
W ``item'' bedzie wartosc 0 - a na ekranie
zobaczymy:
Wejscie do funkcji
Zwrocenie wartosci 0
Wy±wietlanie tych danych pozwala na wyci¡gni¦cie dwóch wa»nych
wniosków:
1
dopiero przy pierwszym
next,
funkcja zako«czyªa swoje dziaªanie w miejscu wyst¡pienia yield.
wej±cie do funkcji jest wykonywanie
wywoªaniu
2
M. Alichniewicz (SKALP)
Python vol. 3
2014
27 / 66
Generatory
Wykonajmy wi¦c po raz kolejny ten sam kod:
item = x.next()
#
#
#
#
#
W ``item'' bedzie wartosc 2 - a na ekranie
zobaczymy:
Wznowienie funkcji
Zwrocenie wartosci 2
Jak wida¢,
funkcja zostaªa wznowiona (a wi¦c nie mo»emy mówi¢ o
wyj±ciu z funkcji, a jedynie o wstrzymaniu - warto±¢ wszystkich
zmiennych jest zachowana!) od miejsca w którym wyszli±my z niej poprzez
sªowo kluczowe
yield, a nast¦pnie znów wstrzymana przy kolejnym
wyst¡pieniu tego sªowa.
M. Alichniewicz (SKALP)
Python vol. 3
2014
28 / 66
Generatory
Mo»emy kontynuowa¢ wywoªywanie
next do uzyskania wszystkich
elementów (uda sie to nam jeszcze 3 razy). Za czwartym razem wynik
wywoªania b¦dzie nieco inny:
item = x.next()
#
#
#
#
#
#
#
#
Wartosc zmiennej ``item'' sie nie zmieni,
a na ekranie zobaczymy:
Wznowienie funkcji
Wyjscie z funkcji
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Wznowili±my dziaªanie, ale te» wyszli±my z p¦tli, a wi¦c nie napotkali±my
na
yield.
Funkcja w naszym przypadku si¦ sko«czyªa, co potwierdza
wypisanie na ekranie ci¡gu Wyjscie z funkcji. Wida¢ równie» »e Python
rzuciª znanym ju» wyj¡tkiem
M. Alichniewicz (SKALP)
StopIteration.
Python vol. 3
2014
29 / 66
Generatory - forma skrócona
Podobnie jak dla instrukcji warunkowych, dla generatorów równie» istnieje
denicja skrócona. Wygl¡da to tak:
(przetworz_element for element in lista)
przetworz_element to oczywi±cie wyra»enie które co± z naszym
elementem (nazwanym przez nas element) robi. Jak wida¢, jest to
konstrukcja b¦d¡ca odpowiednikiem zwini¦tej konstrukcji warunkowej dla
p¦tli for. Przykªad takiego generatora:
a = (item**2 for item in lista)
print repr(a)
# <generator object <genexpr> at 0x7f8e5df83c30>
M. Alichniewicz (SKALP)
Python vol. 3
2014
30 / 66
Generatory - forma skrócona
Tak stworzony obiekt oczywi±cie ma wszystkie potrzebne generatorom
metody -
__iter__ i next.
Problem z takim generatorem jest jeden -
jest to objekt JEDNOKROTNEGO u»ytku. Czyli jak raz co± z nim zrobimy
(np., u»yjemy w p¦tli for), to drugi raz ju» to nie zadziaªa.
W praktyce takie generatory najcz¦±ciej dziaªaj¡ jako skrócona wersja p¦tli
for, i ich celem jest stworzenie specycznej listy. Robi si¦ to umieszczaj¡c
generator w parze nawiasów kwadratowych albo przekazuj¡c do
konstruktora klasy
list:
a = [item**2 for item in list]
# albo, zamiennie
a = list(item**2 for item in list)
M. Alichniewicz (SKALP)
Python vol. 3
2014
31 / 66
Generatory - forma skrócona z instrukcj¡ warunkow¡
Póki co mo»liwo±ci generatorów s¡ w zasadzie identyczne jak funkcji
map,
czyli przeprowad¹ operacj¦ na ka»dym elemencie. Ciekawsze mo»liwo±ci
daje poª¡czenie generatorów ze skrócon¡ instrukcj¡ warunkow¡. Skªadnia
takiego generatora wygl¡da w ten sposób (mo»na to oczywi±cie zwin¡¢ w
jedn¡ lini¦):
(przetworz_element for element in lista\
if warunek_elementu)
if. Jest to warunek
dla ka»dego elementu z caªych danych wej±ciowych.
Jak widzimy, na ko«cu pojawiªa si¦ instrukcja
wykonywany
M. Alichniewicz (SKALP)
Python vol. 3
2014
32 / 66
Generatory - forma skrócona z instrukcj¡ warunkow¡
Prostym przykªadem wykorzystania jest np. znalezienie elementów
wspólnych dla dwóch list:
a = [1, 2, 3, 4, 5]
b = [3, 4, 5, 6, 7]
wspolne = [item for item in a if item in b]
# [3, 4, 5]
Innym praktycznym przykªadem jest konwersja ci¡gu znaków (np.
odczytanych z UART'a) na ich heksadecymentaln¡ reprezentacj¦:
wejscie = 'SKALP'
wyjscie = ''.join('\\x%02X' % ord(char) \
for char in wejscie)
print wyjscie
# \x53\x4B\x41\x4C\x50
M. Alichniewicz (SKALP)
Python vol. 3
2014
33 / 66
Dekoratory
M. Alichniewicz (SKALP)
Python vol. 3
2014
34 / 66
Dekoratory
Cz¦sto, tworz¡c bardziej skomplikowany kod, tramy na przypadek gdzie
np. dla du»ej cz¦±ci metod w klasie pocz¡tkowy kod jest dokªadnie ten sam
- czyli dla przykªadu, jaka± inicjalizacja, sprawdzenie uprawnie«, etc.
Zgodnie z reguªami KISS oraz DRY, warto to gdzie± przenie±¢ na
zewn¡trz. Jednym ze sposobów jest zwyczajne zawarcie tego w jakiej±
dodatkowej metodzie i wywoªywanie na pocz¡tku ka»dej funkcji. To samo
mo»emy zrobi¢ z kodem wywoªywanym na ko«cu.
Tak by to wygl¡daªo w ka»dym j¦zyku programowania. W Pythonie jednak
mo»emy to zrobi¢ nieco inaczej - tworz¡c dekoratory funkcji.
M. Alichniewicz (SKALP)
Python vol. 3
2014
35 / 66
Dekoratory
Przez dekorowanie funkcji rozumiemy wykonanie jakiegos kodu przed i po
wykonaniu zasadniczej tre±ci funkcji. Z kolei sam dekorator jest deniowany
jako specyczna funkcja: przyjmuj¡ca jako parametr funkcj¦, i zwracaj¡ca
now¡, udekorowan¡ funkcj¦.
Zaraz zaraz, jak to przyjmuj¡ca/zwracaj¡ca funkcj¦? Pami¦tajmy: w
Pythonie
wszystko jest obiektem.
Tak wi¦c funkcja te», i mo»emy
spokojnie j¡ przypisa¢ do jakiej± zmiennej - lub te» przekaza¢ do funcji
lub z funkcji. Druga sprawa - w Pythonie mo»emy deniowa¢ funkcje lub
klasy wewn¦trzne, czyli widoczne tylko wewn¡trz funkcji/klasy gdzie
zostaªy zdeniowane.
M. Alichniewicz (SKALP)
Python vol. 3
2014
36 / 66
Dekoratory
Na pocz¡tek zdeniujmy sobie prost¡ funkcj¦ któr¡ b¦dziemy chcieli
udekorowa¢:
def hello():
for i in range(0,10000000):
# Male opoznienie nie zaszkodzi
pass
print "Hello world!"
Funkcja nie jest specjalnie skomplikowana, ale te» dla celów pokazowych
jest jak najbardziej wystarczaj¡ca.
M. Alichniewicz (SKALP)
Python vol. 3
2014
37 / 66
Dekoratory
Zaªó»my »e np chcieliby±my przed p¦tl¡ opó¹niaj¡c¡ wy±wietli¢ tekst
wejscie, a po wy±wietleniu Hello world! dorzuci¢ jeszcze tekst wyj±cie,
jednocze±nie nie modykuj¡c oryginalnej funkcji (celem zachowania
czytelno±ci kodu - funkcja hello ma wy±wietla¢ hello world, a nie dla
przykªadu informowa¢ »e do funkcji weszli±my).
Mo»emy sobie stworzy¢ now¡ funkcj¦, która wywoªa t¡ oryginaln¡:
def hello_decorated():
print 'wejscie'
hello()
print 'wyjscie'
Jest to jedno z mo»liwych rozwi¡za«, ale musimy pami¦ta¢ o wywoªaniu
hello_decorated zamiast hello.
M. Alichniewicz (SKALP)
Python vol. 3
2014
38 / 66
Dekoratory
Na etapie pisania kodu to nie problem. Ciekawiej natomiast zaczyna
si¦ je»eli jest to zmiana tymczasowa - np. jako element debuggowania
programu, i docelowo jej nie b¦dzie w ko«cowym programie. Trzeba b¦dzie
wtedy pami¦ta¢ »eby zmieni¢ X wywoªa« z powrotem na hello. Inny
ciekawy przypadek to sytuacja gdy to nie dotyczy jednej funkcji, a
kilkunastu - wtedy w ogóle mo»emy si¦ pogubi¢. Teoretycznie - mo»emy
zmieni¢ nazw¦ funkcji pierwotnej, np. na _hello, a jako hello
zdeniowa¢ nasz¡ udekorowan¡ funkcj¦. Ponownie schody si¦ zaczn¡ dla
kilkunastu funkcji.
Krokiem który to uªatwi, jest stworzenie nowej funkcji, która generuje
now¡, udekorowan¡ funkcj¦ (czyli, jej argumentem b¦dzie stara funkcja), a
zwróci wersj¦ ze zmianami.
M. Alichniewicz (SKALP)
Python vol. 3
2014
39 / 66
Dekoratory
Stwórzmy wi¦c tak¡ funkcj¦:
def decorator(original_fcn):
"""
Dekoruje funkcje. Jako argument przyjmuje
funkcje oryginalna.
"""
def decorated_fcn(*args, **kwargs):
"""
Funkcja udekorowana. Nie znamy
argumentow, wiec *args i **kwargs.
"""
print 'wejscie'
# Wywolanie oryginalu
original_fcn(*args, **kwargs)
print 'wyjscie'
return decorated_fcn
M. Alichniewicz (SKALP)
# Zwroc nowa funkcje
Python vol. 3
2014
40 / 66
Dekoratory
Je»eli wywoªamy tak¡ funkcj¦ dekoruj¡c¡ jako argument podaj¡c wªa±nie
nasze hello (ale bez nawiasów, czyli nie wywoªujemy jej!), w efekcie
dostaniemy now¡ funkcj¦:
hello_decorated = decorator(hello)
print repr(hello_decorated)
# Dostaniemy cos podobnego do
# <function decorated_fcn at 0x7fbcf45f0938>
Wywoªanie otrzymanego hello_decorated da nam oczekiwany efekt.
Dalej jednak mamy problem, bo trzeba to gdzie± przypisa¢, czyli na
chwil¦ obecn¡ dalej potrzebujemy sztuczki ze zmian¡ nazwy.
M. Alichniewicz (SKALP)
Python vol. 3
2014
41 / 66
Dekoratory
Python i na to ma rozwi¡zanie. Zdeniowana przez nas funkcja decorator
jest faktycznie dekoratorem, i Python posiada mechanizm który umo»liwia
automatyczne dekorowanie funkcji na etapie jej denicji. Maj¡c taki
dekorator, mo»emy sobie przedeniowa¢ nasz¡ funkcj¦ hello:
@decorator # Podaj dekorator
def hello():
for i in range(0,10000000):
# Male opoznienie nie zaszkodzi
pass
print "Hello world!"
Dekorator podaje
przed denicj¡ funkcji lini¡ zaczynaj¡c¡ si¦ od znaku
nazw¦ dekoratora (nie jego wywoªanie) - po
@, a nast¦pnie podajemy
maªpie podajemy po prostu obiekt.
M. Alichniewicz (SKALP)
Python vol. 3
2014
42 / 66
Dekoratory
A co je»eli chcieliby±my mie¢ dekorator, który przyjmuje jaki± dodatkowy
parametr, w naszym przypadku np. tekst jaki chcemy wy±wietli¢ przed
wej±ciem do funkcji?
Z racji tego, »e po @ Python oczekuje na obiekt który jest funkcj¡
(dekoratorem), mo»emy zastosowa¢ sztuczk¦: stworzy¢ funkcj¦, która
przyjmuje jakis parametr, a nast¦pnie zwraca odpowiednio
sparametryzowany dekorator (po maªpie musi by¢ obiekt - ale nigdzie nie
jest powiedziane, »e ma by¢ podany bezpo±rednio.)
M. Alichniewicz (SKALP)
Python vol. 3
2014
43 / 66
Dekoratory
Stwórzmy wi¦c tak¡ funkcj¦ generuj¡c¡ dekorator (docstring-i pomini¦te ze
wzgl¦du na orgraniczone miejsce na slajdzie):
def decorator_verbose(text):
"""
Zwraca dekorator, ktory wyswietla
na wejsciu do funkcji podany tekst.
"""
def decorator(original_fcn):
def decorated_fcn(*args, **kwargs):
print text
# Wywolanie oryginalu
original_fcn(*args, **kwargs)
print 'wyjscie'
return decorated_fcn
# Zwroc nowa funkcje
return decorator
M. Alichniewicz (SKALP)
Python vol. 3
2014
44 / 66
Dekoratory
Oczywi±cie warto sprawdzi¢ jej dziaªanie w praktyce:
@decorator_verbose('Begin execution')
def hello():
for i in range(0,10000000):
# Male opoznienie nie zaszkodzi
pass
print "Hello world!"
hello()
#
#
#
#
#
Wyswietli:
Begin execution
Hello world!
wyjscie
M. Alichniewicz (SKALP)
Python vol. 3
2014
45 / 66
Dekoratory
Dekoratory mo»na równie» ze sob¡ ª¡czy¢ - wtedy funkcja jest bezpo±rednio
udekorowana przez dekorator podany jako ostatni (ten bezpo±rednio
przed denicj¡ funkcji), pó¹niej to co dostaniemy jest dekorowane przez
kolejny dekorator etc. Dorzu¢my wi¦c kolejny dekorator:
def tictoc(fcn):
def decorated(*args, **kwargs):
tic = time.time()
fcn(*args, **kwargs)
# Policz czas wykonywania
toc = time.time()
print "Time elapsed: %f" % (toc - tic)
return decorated
M. Alichniewicz (SKALP)
Python vol. 3
2014
46 / 66
Dekoratory
Czas na przedeniowanie naszego hello:
@tictoc
@decorator_verbose('Begin execution')
def hello():
for i in range(0,10000000):
# Male opoznienie nie zaszkodzi
pass
print "Hello world!"
hello()
#
#
#
#
#
#
Wyswietli: (czas przykladowy)
Begin execution
Hello world!
wyjscie
Time elapsed: 0.380634
M. Alichniewicz (SKALP)
Python vol. 3
2014
47 / 66
Manager kontekstu
M. Alichniewicz (SKALP)
Python vol. 3
2014
48 / 66
Uruchamianie kodu z managerem kontekstu
Manager kontekstu. Brzmi dosy¢ nietypowo, a wi¦c co to jest?
Cz¦±¢ kodu, nawet nie±wiadomie, uruchmiamy w pewnym kontek±cie.
Mo»e to by¢ np. operowanie na pliku - wszystkie operacje wykonujemy w
kontek±cie tego pliku. To, co taki blok charakteryzuje to to, »e musimy
jakie± operacje wykona¢ wchodz¡c do tego kontekstu (np. otwarcie
wspomnianego pliku), tak samo jak musimy wykona¢ pewne operacje
wychodz¡c z tego kontekstu (tutaj - zamkni¦cie pliku).
M. Alichniewicz (SKALP)
Python vol. 3
2014
49 / 66
Manager kontekstu - denicja
W ogólnym rozrachunku, to naszym managerem kontekstu jest - jak
wszystko w Pythonie - nic innego jak specyczny obiekt, posiadaj¡cy
odpowiednie metody. Dla managera kontekstu s¡ to metody dosy¢
intuicyjne - pierwsza to
__enter__ (nie przyjmuj¡ca zewn¦trznych
argumentów) zwracaj¡ca obiekt kontekstu (mo»na samego siebie), druga z
kolei to równie intuicyjne
__exit__, które z kolei przyjmuje 3 argumenty
- deniuj¡ one dlaczego zostaªo zako«czone wykonanie bloku z naszym
kontekstem (dla poprawnego wykonania, wszystkie trzy elementy b¦d¡
miaªy warto±¢
None, dla wyj¡tku b¦d¡ tu dane wyj¡tku).
True.
Je»eli wszystko
poszªo OK, ta metoda powinna zwróci¢
M. Alichniewicz (SKALP)
Python vol. 3
2014
50 / 66
Manager kontekstu - denicja
Zróbmy wi¦c przykªadowy kod. W naszym przykªadzie manager i kontekst
to b¦d¡ dwa ró»ne obiekty.
class ManagerKontekstu():
""" Klasa managera kontekstu """
class Kontekst():
""" Klasa kontekstu """
def __init__(self):
""" Konstruktor kontekstu """
print 'Utworzenie kontekstu'
self.counter = 1
def __init__(self):
""" Konstruktor managera kontekstu """
print 'Utworzenie managera'
# pozostaly kod na nastepnym slajdzie
M. Alichniewicz (SKALP)
Python vol. 3
2014
51 / 66
Manager kontekstu - denicja
def __enter__(self):
""" Wejscie / utworzenie kontekstu """
print 'Wejscie do kontekstu'
self.ctx = ManagerKontekstu.Kontekst()
self.ctx.counter += 1
return self.ctx
def __exit__(self, *args):
""" Wyjscie z kontekstu (sprzatanie) """
print 'Wyjscie z kontekstu'
self.ctx.counter += 1
Zmienna
counter jest zdeniowana »eby pokaza¢ dziaªanie oraz
rozdzielenie kontekstu i managera.
M. Alichniewicz (SKALP)
Python vol. 3
2014
52 / 66
Manager kontekstu - wywoªanie
W Pythonie blok kontekstu jest deniowany nast¦puj¡co:
with ManagerKontekstu() as kontekst:
# zrob cos z kontekstem
# albo wpisz
pass
W naszym zdeniowanym poprzednio kontek±cie mamy wtedy:
if __name__ == "__main__":
with ManagerKontekstu() as kontekst:
print '>>> Zwiekszam kontekst'
kontekst.counter += 1
print 'Counter wynosi: %d' % kontekst.counter
M. Alichniewicz (SKALP)
Python vol. 3
2014
53 / 66
Manager kontekstu - wywoªanie
Je»eli wykonamy nasz kod - dostaniemy na ekranie Counter wynosi: 4.
Zmienna counter nale»y do kontekstu, ale zwi¦kszamy j¡ równie» przy
wchodzeniu do tego kontekstu przez wywoªanie odpowiedniej metody
managera.
Przygotujmy sobie bardziej praktyczny przykªad managera kontekstu
(docsting'i pomi¦te ze wzgl¦du na oszcz¦dno±¢ miejsca):
import time
class TicToc():
def __enter__(self):
self.tic = time.time()
return self
def __exit__(self, *args):
self.toc = time.time()
self.elapsed = self.toc - self.tic
M. Alichniewicz (SKALP)
Python vol. 3
2014
54 / 66
Manager kontekstu - wywoªanie
Sprawd¹my wi¦c jak to dziaªa:
with TicToc() as timer:
# jakies akcje
time.sleep(5) # przerwa na 5 sekund
# jakies dalsze akcje
print 'Czas wykonywania: %f' % timer.elapsed
W efekcie na ekranie powinni±my zobaczy¢ ci¡g podobny do Czas
wykonywania: 5.00281691551 (czas mo»e si¦ ró»ni¢ - ale powinien
by¢ wi¦kszy od 5 w naszym przypadku)
M. Alichniewicz (SKALP)
Python vol. 3
2014
55 / 66
Operacje na plikach
M. Alichniewicz (SKALP)
Python vol. 3
2014
56 / 66
Operacje na plikach
Python, jak zdecydowana wi¦kszo±¢ j¦zyków programowania, umo»liwia
dziaªania na plikach. Do otwarcia pliku sªu»y - dosy¢ logicznie nazwana funkcja
open:
fp = open('nazwa_pliku', 'tryb')
Generalnie, mamy 3 tryby otwarcia pliku:
r
- otwarcie do odczytu - ustawia wska¹nik na pocz¡tek pliku
(domy±lny tryb)
w
- otwarcie do zapisu i
skasowanie aktualnej zawarto±ci pliku -
ustawia wska¹nik na pocz¡tek pliku (je»eli plik nie istnieje, to go
tworzy)
a
- otwarcie do zapisu - ustawia wska¹nik na koniec pliku (je»eli plik
nie istnieje, to go tworzy)
M. Alichniewicz (SKALP)
Python vol. 3
2014
57 / 66
Operacje na plikach
Tryby te mo»na dodatkowo rozbudowywa¢ poprzez dodatkowe
modykatory:
b
- otwiera plik w trybie binarnym (dla systemów gdzie jest
rozró»nienie plik tekstowy / plik binarny)
U
- otwiera plik tekstowy w trybie universal newlines - czyli jako znak
nowej linii na równi jest traktowany
+
\n, \r
oraz
\r\n
(tryb domy±lny)
- otwiera plik do zapisu (je»eli plik nie istnieje, to go tworzy)
M. Alichniewicz (SKALP)
Python vol. 3
2014
58 / 66
Operacje na plikach
Funkcja
open, w przypadku Pythona zwraca obiekt typu le, zamiast
uchwtu o pliku przekazywanego do funkcji odpowiedzialnych za odczyt i
zapis jak w przypadku wi¦kszo±ci j¦zyków programowania. Obiekt typu
le
ma szereg przydatnych do tego metod, zaczynaj¡c od podstawowych:
close() - zamyka plik
write(str) - zapisuje ci¡g znaków do pliku.
Nale»y pami¦ta¢ »e
wywoªanie tej metody nie zawsze powoduje natychmiastowy zapis
zycznie do pliku, czasami jest to tylko zapis do bufora
read(n) - odczytuje n bajtów z pliku.
Je»eli rozmiar nie jest podany,
odczytuje caªy plik (uwaga przy du»ych plikach!)
readline(n) - je»eli nie podano n, odczytuje caª¡ lini¦. Je»eli podane
n, to jest to maksymalna liczba bajtów jaka mo»e by¢ odczytana
jest
(w efekcie mo»emy odczyta¢ niekompletn¡ lini¦).
M. Alichniewicz (SKALP)
Python vol. 3
2014
59 / 66
Operacje na plikach
Ko«cz¡c na tych rzadziej wykorzystywanych:
ush() - wymusza od±wie»enie bufora, tj.
zapis jego zawarto±ci
zycznie na dysk
readlines() - odczytuje wszystkie linie z pliku i tworzy z nich list¦
(odczyt zaczyna od aktualnego wska¹nika)
seek(pointer) - ustawia wska¹nik wewn¡trz pliku (miejsce z którego
b¦dziemy czyta¢ / do którego pisa¢)
tell() - zwraca aktualn¡ warto±¢ wska¹nika
writelines(list) - zapisuje do pliku wszystkie elementy z listy, b¡d¹
innego iterowalnego argumentu podanego w argumencie (czyli mo»e to
np. by¢ generator).
M. Alichniewicz (SKALP)
Metoda nie dodaje znaków ko«ca linii!
Python vol. 3
2014
60 / 66
Operacje na plikach
Jak plik otworzymy, to trzeba go pó¹niej zamkn¡¢ (dajmy innym
programom szans¦ na dost¦p). Poniewa» obiekt typu le ma
zaimplementowane metody
__enter__ oraz __exit__, jest wi¦c
with,
managerem kontekstu - a wi¦c mo»emy wykorzysta¢ tutaj instrukcj¦
która zamknie plik za nas:
# Otworz plik i nie martw sie o zamkniecie
with open('moj_plik', 'w') as fp:
fp.write('Ladna dzis pogoda!\n')
fp.write('Nieprawdaz?')
print fp.closed
# True - czyli plik zamkniety
M. Alichniewicz (SKALP)
Python vol. 3
2014
61 / 66
Zadanie
M. Alichniewicz (SKALP)
Python vol. 3
2014
62 / 66
Zadanie
Napisa¢ program, który odczyta od u»ytkownika wyra»enie matematyczne
(zawieraj¡ce nawiasy, jednak ograniczone do operatorów matematycznych,
bez funkcji - podane w tradycyjnej notacji) oraz obliczy jego warto±¢.
Zadanie trzeba wykona¢ bez wykorzystania funkcji eval!
Podpowiedzi:
Kluczem do rozwi¡zania jest
Odwrotna Notacja Polska (w skrócie -
ONP, albo RPN z angielskiego
Rerverse Polish Notation)
listy mog¡ sªu»y¢ zarówno jako bufor FIFO,
albo jako FILO - czyli stos
Program zbuduj w taki sposób, »eby mo»na byªo go wykorzystywa¢ bez
modykacji w innych projektach (mile widziane obudowanie logiki w klas¦
Wbudowane w Pythona
- uniwersalno±¢ tej klasy, wliczaj¡c w to sposób podania danych - mile
widziane). Mo»na poczyni¢ dodatkowe zaªo»enia przy wprowadzaniu
danych, jak np. biaªe znaki w tre±ci wyra»enia, jednak nale»y pami¦ta¢ »e
ko«cowy u»ytkownik
M. Alichniewicz (SKALP)
nie ma obycia w Pythonie.
Python vol. 3
2014
63 / 66
Materiaªy
M. Alichniewicz (SKALP)
Python vol. 3
2014
64 / 66
Materiaªy
Dokumentacja j¦zyka Python
https://docs.python.org/2/
Forum StackOverow
http://stackoverflow.com/
Improve Your Python: 'yield' and Generators Explained
http://www.jeffknupp.com/blog/2013/04/07/
improve-your-python-yield-and-generators-explained/
Understanding Python Decorators in 12 Easy Steps!
http://simeonfranklin.com/blog/2012/jul/1/
python-decorators-in-12-steps/
Understanding Python's with statement
http://effbot.org/zone/python-with-statement.htm
M. Alichniewicz (SKALP)
Python vol. 3
2014
65 / 66
Spotkania z Pythonem
Cz¦±¢ 3 - wyra»enia lambda, generatory, dekoratory, operacje na plikach
Michaª Alichniewicz
Studenckie Koªo Automatyków SKALP
Gda«sk 2014
Dzi¦kuj¦ za uwag¦!
Na licencji Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License.
M. Alichniewicz (SKALP)
Python vol. 3
2014
66 / 66

Podobne dokumenty