Informatika2-2015/Eloadas 4 Python-4 Referenciak, Objektumok

A MathWikiből
(Változatok közti eltérés)
(Új oldal, tartalma: „= Referenciák, objektumok = == Alapvető fogalmak == Emlékezzünk vissza két előadással korábban amit a mutable típusokról mondtunk. Hogy a listáknál/szót…”)
 
18. sor: 18. sor:
  
 
Ebben az esetben létrehoztam a ''datetime.date'' osztály egy példányát, egy objektumot, ami el tud tárolni egy dátumot, ebben az esetben a mai dátumot. A ''ma'' nevű változó egy referencia, ami erre az objektumra mutat. Megtudható a ''datetime.date'' osztály dokumentációjából, hogy ennek az osztálynak van pl. egy ''year'' nevű tagváltozója, és egy ''weekday()'' nevű metódusa, így ezek elérhetőek a referencián keresztül.
 
Ebben az esetben létrehoztam a ''datetime.date'' osztály egy példányát, egy objektumot, ami el tud tárolni egy dátumot, ebben az esetben a mai dátumot. A ''ma'' nevű változó egy referencia, ami erre az objektumra mutat. Megtudható a ''datetime.date'' osztály dokumentációjából, hogy ennek az osztálynak van pl. egy ''year'' nevű tagváltozója, és egy ''weekday()'' nevű metódusa, így ezek elérhetőek a referencián keresztül.
 +
 +
Az osztályoknak egy speciális metódusa a '''konstruktor'''a. Amikor létrehozom az osztály egy példányát, akkor ezt a metódust hívom meg, tehát az számít, hogy ennek a metódusnak milyen paraméterei vannak. Ennek ellenére csak az osztály nevét kell leírni, és aztán a paramétereket a '''példányosítás'''hoz. Pl. a ''datetime.date'' osztály konstruktorának 3 paramétere az év, hónap és nap, ilyen sorrendben. (Kivéve azt a néhány osztályt aminek speciális szintakszisa van a létrehozáshoz, mint a lista meg a szótár.)
  
 
Itt mindjárt láthatunk is egy fontos különbséget a modulokhoz képest. A modulnál a bele tartozó függvények meghívásához csak a modulra volt szükségem, azt mondhattam a ''math'' modulnál hogy ''math.sin(1)''. Azonban a ''datetime.date'' osztályra nem mondhatom azt hogy ''datetime.date.weekday()'', szükségem van az objektum egy példányára, hogy a metódust azon tudjam meghívni.
 
Itt mindjárt láthatunk is egy fontos különbséget a modulokhoz képest. A modulnál a bele tartozó függvények meghívásához csak a modulra volt szükségem, azt mondhattam a ''math'' modulnál hogy ''math.sin(1)''. Azonban a ''datetime.date'' osztályra nem mondhatom azt hogy ''datetime.date.weekday()'', szükségem van az objektum egy példányára, hogy a metódust azon tudjam meghívni.
  
 
Vannak olyan függvények is, amiket magán az osztályon lehet hívni, és nem egy példányon. Pythonban ezeket ''"class method"''-nek és ''"class attribute"''-nak hívják, ellenben azokkal amiket az előbb említettem, amiket pontosabban ''"instance method"''-nek és ''"instance attribute"''-nak hívnak. Ezeket az "osztálymetódus"-okat akkor használják, ha valamely függvény/változó szervesen kapcsolódik az adott osztályhoz, de nem egy konkrét példányhoz. Lényegében úgy érhetőek el, mint ha lenne az osztály nevével egy azonos nevű modul is, és abban lennének. Itt példa a ''datetime.date.min'', ami a lehetséges legkisebb dátum amit a ''datetime.date'' osztály tárolni tud: ez az érték az osztály minden példányára ugyanaz, és szükségünk lehet már akkor rá amikor még nem hoztunk létre egy példányt se, ezért az osztályhoz tartozik, nem az egyes objektumokhoz. De ez csak plusz lehetőség, továbbra is elérhető az objektumokon keresztül is, pl. a korábbi példában a ''ma.min'' is működne, és ugyanezt adná. Alapból ha ''metódus''ról beszélünk, az "instance method"-öt értjük alatta.
 
Vannak olyan függvények is, amiket magán az osztályon lehet hívni, és nem egy példányon. Pythonban ezeket ''"class method"''-nek és ''"class attribute"''-nak hívják, ellenben azokkal amiket az előbb említettem, amiket pontosabban ''"instance method"''-nek és ''"instance attribute"''-nak hívnak. Ezeket az "osztálymetódus"-okat akkor használják, ha valamely függvény/változó szervesen kapcsolódik az adott osztályhoz, de nem egy konkrét példányhoz. Lényegében úgy érhetőek el, mint ha lenne az osztály nevével egy azonos nevű modul is, és abban lennének. Itt példa a ''datetime.date.min'', ami a lehetséges legkisebb dátum amit a ''datetime.date'' osztály tárolni tud: ez az érték az osztály minden példányára ugyanaz, és szükségünk lehet már akkor rá amikor még nem hoztunk létre egy példányt se, ezért az osztályhoz tartozik, nem az egyes objektumokhoz. De ez csak plusz lehetőség, továbbra is elérhető az objektumokon keresztül is, pl. a korábbi példában a ''ma.min'' is működne, és ugyanezt adná. Alapból ha ''metódus''ról beszélünk, az "instance method"-öt értjük alatta.
 +
 +
Laboron majd fogunk nézni néhány objektum osztályt a beépített könyvtárból.
 +
 +
== Függvény objektumok ==
 +
 +
A python-ban a függvények is objektumok. Hasonlóan a listához és a szótárhoz, speciális szintaxis van a létrehozására, a ''def'' parancs. Nézzünk egy példát:
 +
 +
<wikiframe width="1100" height="700" frameborder="1" src="http://pythontutor.com/iframe-embed.html#code=def+kartya_sorrend(kartya_a,+kartya_b)%3A%0D%0A++++%22%22%22Megmondja+az+elso+kartya+kisebb-e.+%22%22%22%0D%0A++++szin_a,+szam_a+%3D+kartya_a%0D%0A++++szin_b,+szam_b+%3D+kartya_b%0D%0A++++if+szam_a+%3C+szam_b%3A%0D%0A++++++++return+-1%0D%0A++++elif+szam_b+%3C+szam_a%3A%0D%0A++++++++return+1%0D%0A++++else%3A%0D%0A++++++++return+cmp(szin_a,+szin_b)%0D%0A%0D%0Akartyak+%3D+%5B(%22pikk%22,+5),+(%22kor%22,+7),+(%22karo%22,+3)%5D%0D%0Akartyak.sort(cmp%3Dkartya_sorrend)%0D%0Aprint+kartyak%0D%0A%0D%0Aprint+kartya_sorrend.__doc__&origin=opt-frontend.js&cumulative=false&heapPrimitives=false&textReferences=false&py=2&rawInputLstJSON=%5B%5D&curInstr=0&codeDivWidth=500&codeDivHeight=400"/> [http://pythontutor.com/visualize.html#code=def+kartya_sorrend(kartya_a,+kartya_b)%3A%0D%0A++++%22%22%22Megmondja+az+elso+kartya+kisebb-e.+%22%22%22%0D%0A++++szin_a,+szam_a+%3D+kartya_a%0D%0A++++szin_b,+szam_b+%3D+kartya_b%0D%0A++++if+szam_a+%3C+szam_b%3A%0D%0A++++++++return+-1%0D%0A++++elif+szam_b+%3C+szam_a%3A%0D%0A++++++++return+1%0D%0A++++else%3A%0D%0A++++++++return+cmp(szin_a,+szin_b)%0D%0A%0D%0Akartyak+%3D+%5B(%22pikk%22,+5),+(%22kor%22,+7),+(%22karo%22,+3)%5D%0D%0Akartyak.sort(cmp%3Dkartya_sorrend)%0D%0Aprint+kartyak%0D%0A%0D%0Aprint+kartya_sorrend.__doc__&mode=display&origin=opt-frontend.js&cumulative=false&heapPrimitives=false&textReferences=false&py=2&rawInputLstJSON=%5B%5D&curInstr=0 link]
 +
 +
Tegyük fel hogy a program más, már korábban megírt részeiben így használjuk a kártyákat, hogy a színük van előbb, és utána a számuk. De aztán itt szeretnénk a számuk szerint sorba rendezni őket, de a ''sort()'' a párokat magától az első tagja alapján rendezi. A megoldás egy függvény objektum használata.
 +
 +
Egyik dolog amit itt látunk, hogy a lista ''sort()'' metódusának van egy ''cmp'' nevű opcionális paramétere, ami egy függvény objektumot vár. A [https://docs.python.org/2.7/library/stdtypes.html#mutable-sequence-types sort() dokumentációja] leírja, hogy olyan függvényt vár, ami ha a lista két elemét kapja paraméterként, akkor negatív számot ad vissza ha az első kisebb, pozitívat ha a második kisebb, és nullát ha egyenlőek. (Tehát két egész számnál elég lenne a kettő különbségét visszaadni.) Ezért megírtuk így a ''kartya_sorrend()'' függvényt és azt adtuk oda.
 +
 +
A függvény objektumok alapvető tulajdonsága, hogy meghívhatóak. Ez azt jelenti, hogy a függvény neve után tehetek egy zárójelet, abba a függvény paramétereit, és akkor lefut a függvény kódja azokkal a paraméterekkel. De ezen kívül van néhány más tagváltozója és metódusa is, például itt láthatjuk hogy a ''__doc__'' tagváltozóban megtalálható a docstring-ben megadott dokumentáció.
 +
 +
== Lambda függvények ==
 +
 +
Nézzünk egy másik példát: van egy nagy számunk, és egy listában megvan már néhány osztója. Szeretnénk megnézni, hogy mi a szám ami marad ha ezekkel már elosztottuk. Egy lehetséges megoldás:
 +
 +
<python>szam = 30000
 +
osztok = [2, 2, 3, 5]
 +
 +
kisszam = szam
 +
for oszto in osztok:
 +
    kisszam = kisszam / oszto
 +
 +
print kisszam</python>
 +
 +
Ez teljesen legikus, remélem mindenki számára érthető. Most egy alternatív megoldás, ami a beépített [https://docs.python.org/2/library/functions.html#reduce reduce()] függvényt használja:
 +
 +
<python>def oszt(a, b):
 +
    return a/b
 +
 +
kisszam = reduce(oszt, osztok, szam)</python>
 +
 +
Sikeresen elértem, hogy a megoldás 3 sor helyett... továbbra is három sor. De, a függvény objektumok létrehozására nem csak egy speciális szintaxis van, hanem kettő is! Ha egy függvény csak egy sorból áll, ami ''return''-öl valamit, akkor azt így is le lehet írni, lambda függvénnyel:
 +
 +
<python>oszt = lambda a, b : a / b
 +
 +
kisszam = reduce(oszt, osztok, szam)</python>
 +
 +
Ez pontosan ugyanazt jelenti mint az előző változat. De akkor már nem is kell ezt külön eltárolni egy változóba, egyből oda lehet adni paraméternek:
 +
 +
<python>kisszam = reduce(lambda a, b : a / b, osztok, szam)</python>
 +
 +
Így már csak egy soros lett.
 +
 +
Ez fölösleges bonyolításnak tűnhet most számotokra, de amikor az ember már sokezredjére ír kódot ami végigmegy egy listán, akkor próbálja elkerülni :) De komolyabban mondva, a [https://docs.python.org/2/library/functions.html#reduce reduce()], [https://docs.python.org/2/library/functions.html#filter filter()] és [https://docs.python.org/2/library/functions.html#map map()] beépített függvények mind egy-egy sztenderd esetet kezelnek, amihez normálisan végig kéne menni egy listán. Ha ezek közül a függvények közül használjuk valamelyiket, akkor a kód olvasója egyből láthatja, hogy ez az a tipikus eset, míg ha csak azt látná hogy "for", akkor meg kéne néznie a belső kódot is. Így segítheti is az érthetőséget, bár persze a tömörségével nehezíti is.
 +
 +
= Kisebb témák =
 +
 +
== Szöveg formatálás ==
 +
 +
Nézzük a következő kódot, ami egy "osztótáblát" ír ki:
 +
 +
<python>for i in range(1, 6):
 +
    for j in range(1, 6):
 +
        print i / float(j),
 +
    print</python>
 +
 +
Ennek a kimenete így néz ki:
 +
 +
1.0 0.5 0.333333333333 0.25 0.2
 +
2.0 1.0 0.666666666667 0.5 0.4
 +
3.0 1.5 1.0 0.75 0.6
 +
4.0 2.0 1.33333333333 1.0 0.8
 +
5.0 2.5 1.66666666667 1.25 1.0
 +
 +
(Mellesleg, azt hogy a print nem tesz újsort, ha a végén van egy vessző, tudtátok? Hasznos!)
 +
 +
Hát ez lehet hogy egy táblázat, de nem túl átlátható. Mennyivel jobb lenne, ha így nézne ki:
 +
0001.0 0000.5 0.3333 000.25 0000.2
 +
0002.0 0001.0 0.6667 0000.5 0000.4
 +
0003.0 0001.5 0001.0 000.75 0000.6
 +
0004.0 0002.0 01.333 0001.0 0000.8
 +
0005.0 0002.5 01.667 001.25 0001.0
 +
 +
Vagy esetleg így:
 +
1.0    0.5    0.3333 0.25  0.2 
 +
2.0    1.0    0.6667 0.5    0.4 
 +
3.0    1.5    1.0    0.75  0.6 
 +
4.0    2.0    1.333  1.0    0.8 
 +
5.0    2.5    1.667  1.25  1.0 
 +
 +
Többek között erre használható a karakterlánc osztály (angol neve ''string'') [https://docs.python.org/2.7/library/stdtypes.html#str.format format()] metódusa.
 +
 +
[https://docs.python.org/2.7/library/string.html#formatexamples Hivatalos példák.]

A lap 2015. március 4., 10:39-kori változata

Tartalomjegyzék

Referenciák, objektumok

Alapvető fogalmak

Emlékezzünk vissza két előadással korábban amit a mutable típusokról mondtunk. Hogy a listáknál/szótáraknál előfordulhat pl. hogy két változó ugyanarra a listára vonatkozik, ha módosítom az egyiket, a másik is módosul.

link

Ezt értelmezhetjük úgy, hogy van egy lista, ami létezik valahol a számítógép memóriájában, és több változó is ugyanara a listára hivatkozik. Bevezetünk néhány szakkifejezést, ami segít ezeket a fogalmakat kezelni.

Egy ilyen dolog, ami a memóriában valahol létezik, egy objektum. Az objektumnak van egy típusa, vagy más szóval osztálya, ami megmondja, hogy az az objektum éppen lista, szótár, vagy valami más. A konkrét változók amik erre az objektumra hivatkoznak, azok referenciák, és úgy mondhatjuk, hogy erre az objektumra mutatnak.

Az objektumnak a legfontosabb, alapvető, tulajdonsága az osztálya. Ez mondja meg, hogy milyen dolgokat tudunk vele tenni. Például, hogy a listának ki tudjuk venni a valahányadik elemét a szögletes zárójellel, vagy hogy a szótárnak van egy has_key() nevű metódusa, amivel meg lehet nézni hogy egy bizonyos kulcs szerepel-e benne. Hasonlóan egy modulhoz, egy osztály is alapvetően kétfajta elemet tartalmazhat: változókat, ezeket hívhatjuk az osztály tagváltozóinak, és függvényeket, ezeket hívhatjuk az osztály metódusainak.

Nézzünk egy példát:

link

Ebben az esetben létrehoztam a datetime.date osztály egy példányát, egy objektumot, ami el tud tárolni egy dátumot, ebben az esetben a mai dátumot. A ma nevű változó egy referencia, ami erre az objektumra mutat. Megtudható a datetime.date osztály dokumentációjából, hogy ennek az osztálynak van pl. egy year nevű tagváltozója, és egy weekday() nevű metódusa, így ezek elérhetőek a referencián keresztül.

Az osztályoknak egy speciális metódusa a konstruktora. Amikor létrehozom az osztály egy példányát, akkor ezt a metódust hívom meg, tehát az számít, hogy ennek a metódusnak milyen paraméterei vannak. Ennek ellenére csak az osztály nevét kell leírni, és aztán a paramétereket a példányosításhoz. Pl. a datetime.date osztály konstruktorának 3 paramétere az év, hónap és nap, ilyen sorrendben. (Kivéve azt a néhány osztályt aminek speciális szintakszisa van a létrehozáshoz, mint a lista meg a szótár.)

Itt mindjárt láthatunk is egy fontos különbséget a modulokhoz képest. A modulnál a bele tartozó függvények meghívásához csak a modulra volt szükségem, azt mondhattam a math modulnál hogy math.sin(1). Azonban a datetime.date osztályra nem mondhatom azt hogy datetime.date.weekday(), szükségem van az objektum egy példányára, hogy a metódust azon tudjam meghívni.

Vannak olyan függvények is, amiket magán az osztályon lehet hívni, és nem egy példányon. Pythonban ezeket "class method"-nek és "class attribute"-nak hívják, ellenben azokkal amiket az előbb említettem, amiket pontosabban "instance method"-nek és "instance attribute"-nak hívnak. Ezeket az "osztálymetódus"-okat akkor használják, ha valamely függvény/változó szervesen kapcsolódik az adott osztályhoz, de nem egy konkrét példányhoz. Lényegében úgy érhetőek el, mint ha lenne az osztály nevével egy azonos nevű modul is, és abban lennének. Itt példa a datetime.date.min, ami a lehetséges legkisebb dátum amit a datetime.date osztály tárolni tud: ez az érték az osztály minden példányára ugyanaz, és szükségünk lehet már akkor rá amikor még nem hoztunk létre egy példányt se, ezért az osztályhoz tartozik, nem az egyes objektumokhoz. De ez csak plusz lehetőség, továbbra is elérhető az objektumokon keresztül is, pl. a korábbi példában a ma.min is működne, és ugyanezt adná. Alapból ha metódusról beszélünk, az "instance method"-öt értjük alatta.

Laboron majd fogunk nézni néhány objektum osztályt a beépített könyvtárból.

Függvény objektumok

A python-ban a függvények is objektumok. Hasonlóan a listához és a szótárhoz, speciális szintaxis van a létrehozására, a def parancs. Nézzünk egy példát:

link

Tegyük fel hogy a program más, már korábban megírt részeiben így használjuk a kártyákat, hogy a színük van előbb, és utána a számuk. De aztán itt szeretnénk a számuk szerint sorba rendezni őket, de a sort() a párokat magától az első tagja alapján rendezi. A megoldás egy függvény objektum használata.

Egyik dolog amit itt látunk, hogy a lista sort() metódusának van egy cmp nevű opcionális paramétere, ami egy függvény objektumot vár. A sort() dokumentációja leírja, hogy olyan függvényt vár, ami ha a lista két elemét kapja paraméterként, akkor negatív számot ad vissza ha az első kisebb, pozitívat ha a második kisebb, és nullát ha egyenlőek. (Tehát két egész számnál elég lenne a kettő különbségét visszaadni.) Ezért megírtuk így a kartya_sorrend() függvényt és azt adtuk oda.

A függvény objektumok alapvető tulajdonsága, hogy meghívhatóak. Ez azt jelenti, hogy a függvény neve után tehetek egy zárójelet, abba a függvény paramétereit, és akkor lefut a függvény kódja azokkal a paraméterekkel. De ezen kívül van néhány más tagváltozója és metódusa is, például itt láthatjuk hogy a __doc__ tagváltozóban megtalálható a docstring-ben megadott dokumentáció.

Lambda függvények

Nézzünk egy másik példát: van egy nagy számunk, és egy listában megvan már néhány osztója. Szeretnénk megnézni, hogy mi a szám ami marad ha ezekkel már elosztottuk. Egy lehetséges megoldás:

szam = 30000
osztok = [2, 2, 3, 5]
 
kisszam = szam
for oszto in osztok:
    kisszam = kisszam / oszto
 
print kisszam

Ez teljesen legikus, remélem mindenki számára érthető. Most egy alternatív megoldás, ami a beépített reduce() függvényt használja:

def oszt(a, b):
    return a/b
 
kisszam = reduce(oszt, osztok, szam)

Sikeresen elértem, hogy a megoldás 3 sor helyett... továbbra is három sor. De, a függvény objektumok létrehozására nem csak egy speciális szintaxis van, hanem kettő is! Ha egy függvény csak egy sorból áll, ami return-öl valamit, akkor azt így is le lehet írni, lambda függvénnyel:

oszt = lambda a, b : a / b
 
kisszam = reduce(oszt, osztok, szam)

Ez pontosan ugyanazt jelenti mint az előző változat. De akkor már nem is kell ezt külön eltárolni egy változóba, egyből oda lehet adni paraméternek:

kisszam = reduce(lambda a, b : a / b, osztok, szam)

Így már csak egy soros lett.

Ez fölösleges bonyolításnak tűnhet most számotokra, de amikor az ember már sokezredjére ír kódot ami végigmegy egy listán, akkor próbálja elkerülni :) De komolyabban mondva, a reduce(), filter() és map() beépített függvények mind egy-egy sztenderd esetet kezelnek, amihez normálisan végig kéne menni egy listán. Ha ezek közül a függvények közül használjuk valamelyiket, akkor a kód olvasója egyből láthatja, hogy ez az a tipikus eset, míg ha csak azt látná hogy "for", akkor meg kéne néznie a belső kódot is. Így segítheti is az érthetőséget, bár persze a tömörségével nehezíti is.

Kisebb témák

Szöveg formatálás

Nézzük a következő kódot, ami egy "osztótáblát" ír ki:

for i in range(1, 6):
    for j in range(1, 6):
        print i / float(j),
    print

Ennek a kimenete így néz ki:

1.0 0.5 0.333333333333 0.25 0.2
2.0 1.0 0.666666666667 0.5 0.4
3.0 1.5 1.0 0.75 0.6
4.0 2.0 1.33333333333 1.0 0.8
5.0 2.5 1.66666666667 1.25 1.0

(Mellesleg, azt hogy a print nem tesz újsort, ha a végén van egy vessző, tudtátok? Hasznos!)

Hát ez lehet hogy egy táblázat, de nem túl átlátható. Mennyivel jobb lenne, ha így nézne ki:

0001.0 0000.5 0.3333 000.25 0000.2
0002.0 0001.0 0.6667 0000.5 0000.4
0003.0 0001.5 0001.0 000.75 0000.6
0004.0 0002.0 01.333 0001.0 0000.8
0005.0 0002.5 01.667 001.25 0001.0

Vagy esetleg így:

1.0    0.5    0.3333 0.25   0.2   
2.0    1.0    0.6667 0.5    0.4   
3.0    1.5    1.0    0.75   0.6   
4.0    2.0    1.333  1.0    0.8   
5.0    2.5    1.667  1.25   1.0   

Többek között erre használható a karakterlánc osztály (angol neve string) format() metódusa.

Hivatalos példák.

Személyes eszközök