Bevezetés a Python 3-ba # a s z a ka s z na k " v an e gy " k ez d ö p on t ja , s e l f . extrem = Point (x2 , y2) # és "van egy" végpontja d ef __str__( s e l f ) : return ( " S egment : [ ( { : g } , { : g } ) , ( { : g } , { : g } ) ] " . format ( s e l f . ori g . px , s e l f . or ig . py , s e l f . extrem . px , s e l f . extrem . py )) s = Seg ment ( 1 . 0 , 2 . 0 , 3 . 0 , 4 . 0 ) p r i n t ( s ) # Segment : [ ( 1 , 2 ) , ( 3 , 4 ) ]
©
7.8.2. Leszármaztatás Definíció A leszármaztatás az alosztályok specializációval történő létrehozását írja le. Ebben az esetben az öröklési mechanizmust alkalmazzuk. A Python implementáció általában a szülőosztály konstruktorát hívája a leszármaztatott osztály konstruktorában, akár névszerint, akár a super utasítással. Példa
c l a s s Rectan gle : d ef __init__( se lf , leng th =30, width=15): s e l f . L , s e l f . l , s e l f . name = l e ng t h , wi dt h , " r e c t a n g l e " c l a s s Square ( Recta ngle ): # e g y s z e r ü ö r ö k l é s " " " A R e c ta n g l e s z up e r − c l a s s s p e c i a l i z á l t a l o s z t á l y a . " " " d ef __init__( s el f , si de =20): # A Square szuper −c l a s s e −á nak k o n s t r u k to r á t h í v j uk : super () . __init__( sid e , si de ) s e l f .name = " square " # a z a t tr i bu t um ú j r a d e f i n i á l á s a
80
c Hungarian Translation Daróczy Péter
©
Bevezetés a Python 3-ba
8. fejezet Haladó technikák Ez a fejezet bemutat néhány haladó programozási technikát abból a három paradigmából, amit a Python támogat : a procedurális, az objektum és a funkcionális programozásból.
8.1. Procedurális technikák 8.1.1. A dokumentálás javítása A printApi() felhasználói függvény szűri az element rendelkezésre álló metódusait és a kapcsolódó docstring -eket olvashatóbb formában írja ki, mint a help() : def printAp i ( element ): methods = [ el f o r e l in dir (element ) i f n o t el . st ar ts wi th ( ’_’ )] f o r meth in methods : p r i n t ( g e t a t t r ( eleme nt , meth ) . __doc__) i f __name__ == "__main__" : pr in tAp i ( [ ] ) """ L . append ( ob j ec t ) −− a pp end o b j e c t t o end L. count ( val ue ) −> i n t eg e r −− r e t ur n number o f o c c u r r en c e s o f v a lu e L. extend( it er ab le ) −− e xt en d l i s t by a pp en di ng e l e me n t s f ro m t h e i t e r a b l e L. index ( value , [ sta rt , [ stop ] ] ) −> i n t e ge r −− c Robert Cordeau
81
Bevezetés a Python 3-ba r et ur n f i r s t i nd ex o f v al ue . R a is e s V al ue Er ro r i f t he v al ue i s n ot p r es e nt . L . i n s e r t ( i n d ex , o b j e c t ) −− i n s e r t o b je ct b ef o re i nd ex L. pop ( [ index ]) −> item −− remove and r e t ur n i te m a t i n de x ( d e f a u l t l a s t ) . R a i se s I n de xE rr or i f l i s t i s empty o r i nd ex i s o ut o f r an ge . L . remove ( va lu e ) −− remove f i r s t o c cu r re n ce o f v al ue . R a is e s V al ue Er ro r i f t he v al ue i s n ot p r es e nt . L. rever se () −− r e v e r s e ∗ IN PLACE∗ L . s o r t (cmp=None , key=None , r e v e r s e=Fal se ) −− s t a b le s o r t ∗ IN PLACE ∗ ; cmp(x , y) −> −1, 0 , 1 """ ©
8.1.2. Menük készítése szótárral Egy szótár kulcs:érték párját a következő módon használhatjuk fel egy menü implementálására : - a kulcs legyen a menü egy elemének a neve; - a megfelelő érték egy hivatkozás : egy argumentum nélküli eljárás hívása. A példa egy FIFO programozását mutatja be. Ezt az adatstruktúrát egy jegypénztárnál várakozó sor illusztrálja : az első érkezőt szolgálják ki először. A függvénymodul forráskódja : "" " Egy FIFO queue adminisztr á ci ós modulja "" " queue = [ ] # i n i c i a l i z á l á s d ef intoQueue ( ) : queue . append( in t ( inpu t ( " Irj on beegy egé sze t : " ))) d ef fromQueue ( ) : i f len ( queue) == 0: p r i n t ( "\ nLehe tetle n : ür es a sor ! " ) else : p r i n t ( "\nA’%d ’ . elem tö rö lv e van " % queue . pop (0 )) d ef writeQueue ( ) : p r i n t ( " \nqueue : " , queue ) 82
© c Hungarian Translation Daróczy Péter
Bevezetés a Python 3-ba Az adminisztrációs menü forráskódja
" " " Egy FIFO queue implementá l ás a egy l i s t á val . Egy ( sz ót á ra t haszná l ó ) menü argumentum n é l k ü l i e l j á r á s o ka t h í v """ # import from queue_FIFO_menu_m import intoQueue , fromQueue , writeQueue # main program −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− writeQueue () CMDs = { ’ a ’ : intoQu eue , ’ v ’ : writeQu eue , ’ s ’ : fromQueue} menu = " " " (A)ppend (V)iew (S)upprimer (Q) ui t Your choice ? """ while True : while True : tr y : ch oi ce = inpu t (menu ) . st r i p ( ) [ 0 ] . lower () except : choice = ’q ’ i f c h o i c e n o t i n ’ avsq ’ : p r i n t ( " In va li d opt ion ! Tryaga in " ) else : break
i f cho ice == ’q ’ : p r i n t ( " \Qu it ! " ) break CMDs[ ch oi ce ] ( )
c Robert Cordeau
© 83
Bevezetés a Python 3-ba
8.1.3. Rekurzív függvények Definíció Egy rekurziv függvény hívhatja önmagát. Például egy N elemű táblázat elemeinek növekvő sorrendbe rendezéseelőször kiválasztjuk a legkisebb elemet, majd rendezzük a maradék N-1 elemű táblázatot. Bár gyakran nehezebb megérteni és kódolni ezt a módszert, mint a klasszikus iterratívnak nevezett módszert, bizonyos esetekben ez a módszer a legközvetlenebb alkalmazása a matematikai definíciónak. A következő klasszikus példa a faktoriális függvény definíciója rekurzióval 1:
1, n! = n ∗ (n − 1)!,
ha n = 0 , ha n ≥ 1
Megjegyzés A matematikai definíciót nagyon pontosan másolja a kód :
d ef f a c t o r i a l ( n ) : i f n == 0: # a l a p e se t : a b e f e j e z é s f e l t é t e l e return 1 e l s e # r e ku r z í v e s e t return n ∗ f a c t o r i a l ( n−1) © Ebben a definícióban az n! értéke nem ismert, amíg el nem érjük a rekurzió befejező feltételét (itt n = = 0). A program mindaddig halmozza a rekurzív függvényhívásokat, míg el nem éri a befejezés feltételét, majd a legutolsó függvényhívástól kezdve kiszámolja a függvényértékeket. Az ábra ezt a mechanizmust szemlélteti.
1
Szemben az iterrativ definícióval : n! = n*(n-1)*(n-2)*...*2*1
84
c Hungarian Translation Daróczy Péter
Bevezetés a Python 3-ba
8.1. ábra. A 4! rekurzív kiszámításának modellje
8.1.4. A generátorok és a generátor-kifejezések A generátorok A generátorok egy olyan eszközt adnak a kezünkbe, amivel megvalósítható a késleltetett kiértékelésnek ("lazy evaluation " vagy call-by-need ) nevezett kiértékelési stratégia. Ennek az a lényege, hogy mindaddig késlelteti egy kifejezés kiértékelését, amíg az értékre ténylegesen szükség lesz. Ez sokkal hatékonyabbnak bizonyulhat (memóriahasználatban), mint például egy nagy lista egyszerre történő kiszámolása. A következő generátorpélda annyi értéket ad meg, amennyit kérünk : def qua rte rs ( next_quarter =0.0 ): while True : y i e l d n e xt _ qu a rt e r next_quarter += 0.25 i f __name__ == "__main__" : result = [] for x in quarters () : re su lt . append(x ) i f x == 1 . 0 : break c Robert Cordeau
85
Bevezetés a Python 3-ba p r i n t ( "Eredmé ny li st a : " , re su lt ) # Eredmé n y l i s t a : [ 0 . 0 , 0 . 2 5 , 0 . 5 , 0 . 7 5 , 1 . 0 ]
©
Egy kezdőérték is átadható a generátornak : # import import s ys d ef qua rte rs ( next_quarter =0. 0): while True : r e c e i v e d = ( y i e l d n e xt _ qu a rt er ) i f r e c e i v e d i s None: next_quarter += 0.25 else : next_quarter += received i f __name__ == "__main__" : result = [ ] generator = quarters () while l e n ( r e s u l t ) < 5 : x = next ( generat or ) i f a b s ( x − 0 . 5 ) < s y s . f l o a t _ i n f o . e p s i l o n : x = genera tor . send (1 .0 ) # ú j r a i n i c i a l i z á l j a a g en er á t o r t re su lt . append(x ) p r i n t ( "Eredmé n yl i s ta : " , re s u l t ) # Eredmé n y l i s t a : [ 0 . 0 , 0 . 2 5 , 1 . 5 , 1 . 7 5 , 2 . 0 ] © A generátorkifejezések Szintaxis Egy generátorkifejezésnek majdnem azonos a szintaxisa a szűkített listák szintaxisával; a különbség az, hogy egy generátorkifejezés zárójelbe van zárva.
8.1.5. Egymásba ágyazott függvények (closure) A Pythonban a függvénydefiníció szintaxisa megengedi a függvénydefiníciók egymásba ágyazását. Két alkalmazást különböztetünk meg : 86
c Hungarian Translation Daróczy Péter
Bevezetés a Python 3-ba • A függvénygyár-idióma visszatérési értéke egy beágyazott függvény :
d ef create _plus (add ) : " "" Függvé nygyár " " " d ef pl us ( incremen t ): " " " B eá g y a z o t t f ü g gv é ny : a c r e a t e_ p l u s ( ) l o k á l i s n e v e i t h as zn á l j a . " " " return increment + add r e t u r n plus
# Föprogram −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ## ké t kül önbözö f üggvénygyár gener á l ása p = create_plus (23) q = create_plus (42) ## a lka lmaz á s p r i n t ( "p(100 )=" , p(1 00)) p r i n t ( "q (100) =" , q( 100) )
©
• A függvénygyár egy osztályt ad visszatérési értékül :
# classes c l a s s CaseNormal ( ob je ct ) : def aMethod( s e l f ) : p r i n t ( " normal " )
c l a s s CaseS pecia l ( ob jec t ) : def aMethod( s e l f ) : p r i n t ( " sp ec ia l " ) # function d ef ca se Is Su it ab le Fo r ( isNormal=True ): "" "Függvénygyár , aminek vi ss za t ér é si é rt éke e gy o s z t á l y . " " " return CaseNormal() i f isNormal e l s e CaseSpecial () # Föprogram #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− a n _i n st a nc e = c a s e I s S u i t a b l e F o r ( ) an_insta nce . aMethod () # normal o t h e r _ in s t a n c e = c a s e I s S u i t a b l e F o r ( F a l s e ) other _inst ance . aMethod () # s p e c i a l c Robert Cordeau
© 87
Bevezetés a Python 3-ba
8.1.6. A dekorátorok A Python dekorátorok olyan függvények, amik egy függvény, metódus vagy osztály hívásakor lehetővé teszik előzetes műveletek végrehajtását. Szintaxis Legyen deco() egy dekorátor. Egy függvény dekorálásához a következőt kell írnunk:
@deco d ef fu nct ion ( arg1 , arg2 , . . . ) : pass
©
Ez az írásmód egyenértékű a következö függvénykompozícióval : d ef fu nct ion ( arg1 , arg2 , . . . ) : pass f u n c t i o n = d ec o ( f u n c t i o n ) #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Egy t ö b b sz ö r ö s e n d ek o r á l t g f ü g gv é ny p é l d á j a @f1 @f2 @f3 d ef g ( ) : pass # A j e l ö l é s e gy en é r t é kü a k ö v e t ke z ö v e l : g = f 1 ( f 2 ( f 3 ( g ) ) ) ©
Egy egyszerű példa : # függvények d ef unDecorateur ( f ): d ef _int erne ( ∗ args , ∗∗ kwargs ) : p r i n t ( " Dekorá lt f üggvé ny ! " ) return f ( ∗ args , ∗∗ kwargs) return _interne @unDecorateur d ef uneFonction(a , b ): return a + b d ef autreFonction (a , b ): return a + b 88
c Hungarian Translation Daróczy Péter
Bevezetés a Python 3-ba
# Föprogram =============================================== ## dekorá tor haszná lat a p r i n t ( uneFonction (1 , 2)) ## f üggvénykompoz í ci ó haszná la ta autreFo nction = unDecorateur( autreFo nction ) p r i n t ( autreFo nction (1 , 2)) """ Dekorá lt függvény ! 3 Dekorá lt függvény ! 3 """ ©
8.2. Technikai objektumok Amint azt az előző fejezetben láttuk, a Python teljesen objektum orientált nyelv. Minden alap- illetve leszármaztatott típus osztályként implementált absztrakt adattípus. Ezen osztályok mindegyike egyetlen szülőosztályból származik, az object osztályból.
8.2.1. __slots__ és __dict__ Vizsgáljuk meg a következő kódot : c l a s s Poin t : __slot__ = ( " ’x " ’ , " ’y " ’) def __init__( s e l f , x=0,y=0): s e l f . x=x s e l f . y=y © Amikor egy osztályt a __slot__ utasítás alkalmazása nélkül hozunk létre, ahogyan azt idáig tettük, a Python minden osztálypéldány számára transzparens módon létrehoz egy __dict__ nevű privát szótárat és ez a szótár tartalmazza a példányok attributumait. Ez a magyarázat arra, hogy miért lehet egy objektum attributumaihoz újabbakat hozzátenni illetve belőlük törölni. Ha viszont megelégedünk olyan objektumokkal, melyek attributumaihoz se hozzátenni, sem pedig belőlük elvenni nem akarunk, akkor létrehozhatunk privát c Robert Cordeau
89
Bevezetés a Python 3-ba szótár nélküli osztályokat, ami minden egyes objektumgeneráláskor memóriát fog megtakarítani. Ezt valósítja meg az alábbi példa egy __slot__ osztályattributum definiálásával, aminek az értéke egy tuple, amit az attributumok nevéből áll.
8.2.2. A functor A Pythonban egy függvényobjektum vagy functor egy hivatkozás minden hívható objektumra : függvényre, lambda-függvényre, metódusra, osztályra. A beépített callable() függvénnyel tesztelhetjük ezt a tulajdonságot : >>> d ef myFunction () : p r i n t ( ’This i s " ca l la b le " ’ ) >>> c a l l a b l e ( myFunction ) True >>> chain = ’ This i s " ca l l ab le " ’ >>> c a l l a b l e ( cha ine ) False
©
Egy osztály példányait átalakíthatjuk functor-okká, ha az osztályban definiálva van a __call__() speciális metódus : >>> c l a s s A: def __call__( se lf , un , deux ) : return un + deux >>> a = A() >>> ca ll a bl e (a ) True >>> a (1 , 6) 7
©
8.2.3. Az accessor-ok Az egységbezárás problémája Az objektum orientált paradigmában egy objektum állapota privát, más ob jektumoknak nincs joguk azt sem megnézni, sem módosítani. Hagyományosan a következő láthatóságokat különböztetjük meg : 90
c Hungarian Translation Daróczy Péter
Bevezetés a Python 3-ba • public; • protected; • private.
A Pythonban minden attributum (adatok, metódusok) public ! Mostantól finomíthatunk ezen a tényen. Egy egyszerű és hasznos megállapodás : az egy aláhúzás karakterrel kezdődő neveket fogjuk a protected attributum jelölésére használni. Például : _attrib. A Python semmire sem kötelez, a fejlesztőn múlik a megállapodás betartása ! A Python javasol egy mechanizmust (name mangling ) a private attributumok emulálására : az osztályazonosítók két aláhúzás-karakterrel (__) kezdődnek. Például : __ident. Ez a védelem deklaratív marad és nem nyújt abszolút védelmet. A
property
megoldás
Az egységbezárás elvét a tulajdonság fogalmával dolgozták ki. Definíció Egy tulajdonság property egy példány-attributum, aminek speciális funkciói vannak. Ezt a megoldást két szintaxis implementálja. Az első explicit módon definiálja az x tulajdonságot és négy paraméterét (rendre) : c l a s s C: d ef __init__( s e l f ) : self . _ma_propriete = None d ef getx ( s e l f ): "" " gett er . "" " return s e l f . _x d ef s e t x ( s e l f , v a l u e ) : """ sett er . "" " s e l f . _x = v a l u e c Robert Cordeau
91
Bevezetés a Python 3-ba d ef delx ( s e l f ): "" " del ete r . "" " d el s e l f . _x x = property ( getx , setx , delx , "Envagyokaz ’x ’ tu la jd on s ág . " ) # auto − t e s t =============================== i f __name__ == ’__main__’ : t e s t = C( ) t e s t . x = 10 # s e t t e r p r i n t ( te st .x ) # g e t t e r p r i n t (C. x .__doc__) # documentation """ 10 En v ag yo k a z ’ x ’ t u l a j d o n s á g . """
©
A második, egyszerűbb a dekorátorok szintaxisát alkalmazza. Megjegyzem, hogy a property dokumentációs stringje itt az x tulajdonság docstringje : c l a s s C: d ef __init__( s e l f ) : s e l f ._x = None @property d ef x ( s e l f ) : " " " En v ag yo k a z ’ x ’ t u l a j d o n s á g . " " " return s e l f . _x @x. se tt er d ef x ( s e l f , v a lu e ) : s e l f . _x = v a l u e @x. dele te r d ef x ( s e l f ) : 92
c Hungarian Translation Daróczy Péter
Bevezetés a Python 3-ba d e l s e l f . _x # auto − t e s t =============================== i f __name__ == ’__main__ ’ : t e s t = C () t e s t . x = 10 # s e t t e r p r i n t ( te st .x ) # g e t t e r p r i n t (C. x . __doc__) # documentation """ 10 En v ag yo k a z ’ x ’ t u l a j d o n s á g . """
©
8.3. Függvénytechnikák 8.3.1. A lambda direktíva Funkcionális nyelvek (mint amilyen a Lisp) konstrukciója, a lambda direktíva lehetővé teszi egy anonim függvény definiálását, aminek a visszatérési értéke egy kifejezésre van korlátozva. Szintaxis lambda [paraméterek] :kifejezés Például ennek a függvénynek "s" a visszatérési értéke, ha az argumentuma nem 1, különben egy üres karakterlánc :
s = lambda x : " " i f x == 1 e l s e " s " További példák :
©
>>> def makeIncrementor(n): retu rn lambda x : x+n >>> f2 = makeIncrementor (2 ) >>> f6 = makeIncrementor (6 ) c Robert Cordeau
93
Bevezetés a Python 3-ba >>> p r i n t ( f2 (3) , f6 (3) ) 5 9 >>> >>> L = [ lambda x : x ∗ ∗ 2 , lambda x : x ∗ ∗ 3 , lambda x : x ∗ ∗ 4 ] >>> f o r f in L : p r i n t ( f (2 ) , end=" " ) 4 8 16
8.3.2. A map, filter és reduce függvények A funkcionális programozás egy programozási paradigma, ami a számításokat matematikai függvények kiértékelésének tekinti és elutasítja az állappotváltozást és az adatok megváltoztatását. A függvények alkalmazását hangsúlyozza, szemben az imperatív programozással, ami az állapotváltozásokat helyezi előtérbe 2. A funkcionális paradigma nem használ állapotokat egy program leírására, hanem egymásba ágyazott függvényeket, amiket úgy tekinthetünk mint "fekete dobozokat", amiket egymásba ágyazhatunk. Minden doboznak több bemeneti paramétere van, de csak egyetlen kimenete van, minden bemeneti érték n-esre csak egy lehetséges kimeneti értéket adhat ki. Így a függvények nem bezetnek be mellékhatásokat. A program tehát egy alkalmazás matematikai értelemben, ami csak egyetlen eredményt ad minden egyes bemeneti értékegyüttesre 3 . A funkcionális programozás három fogalmon nyugszik : mapping , filtering és reducing , amiket a Pythonban három függvényel implementáltak : map(), filter() és reduce(). A
függvény : map() - egy függvényt alkalmaz egy szekvencia minden elemére és visszatérési értékként egy iterrátort ad : >>> d ef ren voi Tit res ( element ): return element . ti t l e () map()
>>> map( ren voi Tit res , [ ’ f i a t lu x ’ , " lor d of the f l y " ] )