Kihagyás

Pyhton

1. Bevezetés

A Python egy általános célú, nagyon magas szintű programozási nyelv, melyet Guido van Rossum holland programozó kezdett el fejleszteni 1989 végén, majd hozott nyilvánosságra 1991-ben. A nyelv tervezési filozófiája az olvashatóságot és a programozói munka megkönnyítését helyezi előtérbe a futási sebességgel szemben. Például a behúzások szintaktikailag is fontosak.

A Python többek között a funkcionális, az objektumorientált, az aspektusorientált az imperatív és a procedurális programozási paradigmákat támogatja. Dinamikus típusokat és automatikus memóriakezelést használ, ilyen szempontból hasonlít a Scheme, Perl és Ruby nyelvekhez, emellett szigorú típusrendszerrel rendelkezik. Erőssége a gazdag szabványos programkönyvtár.

A Python úgynevezett interpreteres nyelv, ami azt jelenti, hogy nincs különválasztva a forrás- és tárgykód, a megírt program máris futtatható, ha rendelkezünk a Python értelmezővel. A Python értelmezőt számos géptípusra és operációs rendszerre elkészítették, továbbá számtalan kiegészítő könyvtár készült hozzá, így rendkívül széles körben használhatóvá vált.

Az egyik legnépszerűbb programozási nyelv. Nyitott, közösségalapú fejlesztési modellt mutat fel, amit a közhasznú Python Software Foundation felügyel, ami a nyelv definícióját a CPython referenciaimplementációval gondozza.

2. Története

A Python alapötlete az 1980-as évek végén született meg. A fejlesztést 1989 decemberében kezdte el Guido van Rossum a CWI-n. A CWI (Centrum Wiskunde & Informatica, magyarul Matematikai és Informatikai Központ) egy kutatóintézet Amszterdamban. A nyelv a nevét a Monty Python csoportról kapta. 1991 februárjában jelent meg az első nyilvános változat (0.9.0 verzió néven) az alt.sources hírcsoportban. 1994-ben jött létre a comp.lang.python hírcsoport, ami egy jelentős mérföldkő volt a nyelv fejlődésében. Szintén 1994-ben látott napvilágot az 1.0 verzió, amit az ezredfordulón, 2000 októberében követett a Python 2.0, majd pedig 2008-ban a Python 3.0. Már a Python 1-es verziója bírt bizonyos funkcionális képességekkel, melyeket azonban később kivezettek belőle. Kilenc alverziója jelent meg. A Python 2-es verziója vezetett be olyan képességeket, mint a ciklusfelismerő szemétszedés, a referenciaszámolás, a list comprehension és a Unicode támogatás. A Python 2 utolsó hivatalos verziója a 2.7.18, ami 2020-ban jelent meg. A 2.6-os verzióban bevezettek egy eszközt, amivel meg lehetett nézni, hogy mely kódszakaszokat nem támogatnak majd a jövőben. A főbb verziók nem kompatibilisek egymással.

A Python 3.0 több újdonságát visszavezették a Python 2.6-ba és Python 2.7-be. Az áttérés megkönnyítése érdekében a Python 3-ba berakták a 2to3 képességet, amivel automatizálni lehet az áttérést. Eredetileg a Python 2.7 támogatását 2015-ben akarták befejezni, ám ezt elhalasztották 2020-ig, a már létező Python kódok sokasága miatt, és azért, mert az új verzió nem kompatibilis vele, így portolásra van szükség. Amellett, hogy nem adnak ki újabb javításokat, még biztonsági frissítések sem jelennek meg a 2.7 számára. Ezzel a legkorábbi támogatott verzió a 3.6 lett.

Azóta a 3.6-os verzió támogatását megszüntették. 2021-ben felgyorsították a 3.8.8 és a 3.9.2 megjelenését,, mivel az összes Python verzióval biztonsági problémák adódtak, beleértve a 2.7-et is. Ez lehetővé tette a távoli kódvégrehajtást és a cache-mérgezést.

2022-ben a 2022, Python 3.10.4 és 3.9.12 kiadását hozták előre, és a 3.8.13 és 3.7.13 verziókat is gyorsítva adták ki, szintén biztonsági problémák miatt. Amikor 2022 májusában megjelent a 3.9.13-as verzió, bejelentették, hogy a 3.9-es sorozat ezután csak biztonsági frissítéseket fog kapni. 2022 szeptember 7-én a 3.10.7, 3.9.14, 3.8.14 és 3.7.14 jelent meg, mivel az előző verziók sérülékenynek bizonyultak a szolgáltatásmegtagadási támadásokkal szemben.

Az elavult smtpd modult el fogják távolítani a 3.12-es verzióból. További régi, elavult, már nem működő osztályokat és metódusokat is eltávolítanak, például a unittest modulból. Eltávolították a wstr és a wstr_ modulokat is, az UTF-8 alapértelmezetté tétele miatt.

3. Tervezési filozófia és képességek

Guido van Rossum, a Python tervezője, az OSCON konferencián 2006-ban

A Python egy többparadigmás programozási nyelv. Az objektumorientált és a strukturált programozás teljes támogatást élvez, a funkcionális programozást és az aspektusorientált programozást több képességgel támogatja, köztük metaprogramozással és metaobjektumokkal is. Kiegészítőkkel elérhetők további paradigmák, mint a szerződés alapú tervezés vagy a logikai programozás. Mindez lehetővé teszi, hogy a programozók azt a megközelítést válasszák, amelyiket akarják, vagy amit az adott feladathoz célszerűnek találnak.

A Python dinamikus típusozást használ, de a 3.5-ös verziótól kezdve annotációban lehet jelezni a típusokat, hogy a külső szofverek által igényelt típus-kikövetkeztetést megkönnyítsék. A szemétszedés referenciaszámláláson és ciklusfelderítésen alapul. A névfeloldás dinamikus (késő kötés), ami azt jelenti, hogy a metódusnevek és a változónevek a program végrehajtása során kapcsolódnak össze.

A Lisp hagyományai alapján funkcionális képességekkel bír, mint a filter, map, reduce függvények, a list comprehension, szótárak, halmazok és generátor kifejezések. A standard könyvtár tartalmaz a Haskell nyelvtől és a Standard ML-től átvett funkcionális eszközöket (itertools és functools).

Magfilozófiáját a The Zen of Python (PEP 20) összegzi. A legfontosabb aforizmák:

  • A szép jobb, mint a csúnya.
  • Az explicit szebb, mint az implicit.
  • Az egyszerű szebb, mint a bonyolult.
  • A bonyolult jobb, mint a komplikált.
  • Az olvashatóság számít.

Ahelyett, hogy az összes képességet beépítették volna a magba, a Pythont úgy tervezték, hogy bővíthető legyen. Ez a kompakt modularitás népszerűvé vált, mint programozható interfészek hozzáadása már létező alkalmazásokhoz. Van Rossum egy kis magnyelvet és egy áttekinthető, nagy standard könyvtárat vizionált könnyen bővíthető értelmezővel, mivel frusztrálta az ABC ellenkező megközelítése. Ez lehetővé teszi, hogy Python kódból olyan modulokat hívjanak, amelyek más nyelven íródtak. Így például az időkritikus részek áttehetők C-be. Megfordítva, Python modulok és pluginok írhatók más programok számára. Így használják a következőkhöz: Blender, Cinema 4D, GIMP, Maya, OpenOffice illetve LibreOffice, PyMOL, SPSS, QGIS vagy KiCad.

A Python egyszerű, átlátható szintaxisra törekszik, míg meghagyja a választás szabadságát a különböző programozási metodológiák között. A kulcsszavak számát is alacsonyan tartják az egyszerűség és átláthatóság érdekében. Szemben a Perl megközelítésével, hogy többféleképpen is meg lehet csinálni, a Python azt a gondolatot karolja fel, hogy legyen egy nyilvánvaló mód megtenni valamit, és ez lehetőleg az egyetlen mód legyen. Alex Martelli, a Python Foundation tagja és Python könyvszerző szerint Python kultúrában valamit okosnak nevezni nem számít bóknak.

A Python fejlesztői arra törekszenek, hogy elkerüljék a korai optimalizációt, és elutasítják a CPython referencia implementáció nem kritikus részeinek patcheit, melyek kisebb sebességnövekedést eredményeznek az érthetőség csökkentése mellett. Ha fontos a sebesség, akkor a Python fejlesztő az időkritikus függvényeket átteszi egy olyan modulba, melyet egy gyorsabb nyelven írtak, például C-ben. További lehetőségek a PyPy, illetve a Cython használata. A Cython a Python kódot C-re fordítja, és közvetlen C-szintű API-hívásokat használ.

A Python fejlesztői arra is törekszenek, hogy a nyelv használata örömet okozzon. Ezt tükrözi a névválasztás is: a név forrása a Monty Python, és a tutorialok és referenciaanyagok alkalmankénti játékos megközelítése, például a ham (sonka) és eggs (tojás(ok)) szavak használata az általános foo és bar helyett. Ez utalás a Monty Python egy játékára.

Amellett, hogy Guido van Rossum kedvelte a Monty Python társulatot, a nevet úgy választotta, hogy a név legyen rövid, egyértelmű és misztikus. Ezzel szemben a logó a kígyókkal való asszociációt támogatja, ami megnyilvánul a Python által befolyásolt Cobra nevének választásában és a Boa eszközkészlet elnevezésében.

Az angol nyelvű Python közösségben több neologizmus is született. A pythonic szó azt jelenti, hogy jól használják a Python idiómákat, a nyelvet folyékonyan, illetve természetesen használják, megfelelnek a minimalista filozófiának és az olvashatóságot is fontosnak tartják. Ezzel szemben egy nehezen érthető vagy egy nyersfordításnak kinéző kód unpythonic. A Python programozókat, különösen a tapasztalt szakértőket pedig Pythonistasnak nevezik.

4. Szintaxis és szemantika

A nyelv egyik alapkoncepciója, hogy könnyen olvasható legyen. Formázása vizuálisan folyékony, és gyakran angol kulcsszavakat használ ott, ahol sok más nyelv központozást használ. Ennek célja az olvashatóság javítása. Sok más nyelvtől eltérően nem használ kapcsos zárójeleket a blokkok elhatárolására, és pontosvesszőt sem kötelező kitenni az egyes utasítások után, habár nem tilos. Viszont ha több utasítást írnak egy sorba, akkor azokat pontosvesszővel kell elválasztani. Kevesebb a szintaktikai kivétel és speciális eset, mint a C-ben vagy a Pascalban.

A Python nyelv behúzásokat használ a blokkok elkülönítésére kulcsszavak vagy kapcsos zárójelek helyett. Bizonyos utasítások, amelyek új blokkot nyitnak, a behúzás növelését követelik meg. A blokk addig tart, amikor a behúzás újra kisebb. Így a szkriptek szintaktikai szerkezetét a látható struktúra is reprezentálja. Angolul ezt a szabályt néha off-side rule néven emlegetik. Még néhány nyelv használja ezt a szabályt, mint például a Haskell és a Miranda, de a legtöbb nyelvben a behúzásnak nincs szemantikus jelentése. Egy programban a behúzás alapegységének egységesnek kell lennie, különben az szintaktikai hiba, melyet az értelmező IndentationError néven jelez. A szóközök és a tabulátorok keverése esetén elvész a hordozhatóság. Ugyanis a tabulátorok és a szóközök közötti átváltási arány a szövegszerkesztő, illetve az értelmező beállításától függ. A tabnanny modul segít megtalálni és megszüntetni a szóközök és a tabulátorok keverését. Konvenció szerint a behúzás alapegysége négy szóköz. A behúzás e célú használata Peter J. Landintól származik.

5. Egyszerű adattípusok

A Python különbséget tesz a mutálható és a mutálhatatlan típusok között. Ezek a fogalmak első közelítésben a megváltoztathatóságra utalnak, ám a pontos különbség ennél finomabb.

A Python kacsa-típusozást (duck-typing) használ. A változónevek nem típusozottak, de az objektumok igen. A típusmegkötéseket nem ellenőrzi fordítási időben; ha az adott típuson nem végezhető el az adott művelet, akkor az futási időben derül ki. A dinamikus típusozás azt jelenti, hogy az adattípus az értékhez és nem a változóhoz kötődik.A dinamikus típusozás mellett a nyelv erősen típusos, ami azt jelenti, hogy a nem jóldefiniált műveletek nem végezhetőek el. A típusvizsgálat szigorúbb, mint a Perl, de kevésbé szigorú, mint az Objective CAML esetén.

A Pythonban osztályokkal lehet újabb típusokat definiálni. Új példányok az osztály hívásával hozhatók létre, például SpamClass() vagy EggsClass(). Minden objektum. Objektumok a modulok, az osztályok, típusok és metódusok is. Az osztályok a metaclass típus példányai, ami a saját példánya. Ez lehetővé teszi a reflexiót és a metaprogramozást.

A 3.0 verzió előtt kétféle osztály volt, régi stílusú és új stílusú. A 3.x sorozat csak az új stílusú szemantikát támogatja.

A hosszú távú tervek között szerepel a graduális típusozás támogatása. A Python szintaxisa lehetővé teszi statikus típusok használatát, azonban ezeket nem ellenőrzi az alapértelmezett CPython implementáció.

5.1. A Python 3 beépített típusai

Típus Mutabilitás Leírás Példák
bool immutable Kétértékű logikai adattípus True
False
bytearray mutable Bájtok sorozata bytearray(b'Some ASCII')
bytearray(b"Some ASCII")
bytearray([119, 105, 107, 105])
bytes immutable Bájtok sorozata b'Some ASCII'
b"Some ASCII"
bytes([119, 105, 107, 105])
complex immutable Komplex szám adattípus valós és képzetes részével 3+2.7j
3 + 2.7j
dict mutable Hasítótábla, kulcs-érték párokat tartalmaz {'key1': 1.0, 3: False}
{}
types.EllipsisType immutable Ellipszis helyőrző ...
Ellipsis
float immutable Lebegőpontos szám 1.33333
frozenset immutable Rendezetlen halmaz adattípus frozenset([4.0, 'string', True])
int immutable Tetszőleges méretű egész szám adattípus 42
list mutable List típus, eltérő típusokat is tartalmaz [4.0, 'string', True]
[]
types.NoneType immutable Érték hiányát jelző objektumtípus None
types.NotImplementedType immutable Helyőrző, amit túlterhelt operátorok adhatnak vissza NotImplemented
range immutable Számok szekvenciája range(-1, 10)
range(10, -5, -2)
set mutable Rendezetlen halmaz adattípus {4.0, 'string', True}
set()
str immutable Unicode string 'Wikipedia'
"Wikipedia"
"""Többsoros string"""
Többsoros string
tuple immutable Rendezett n-es (4.0, 'string', True)
('single element',)
()

6. Aritmetika

A Python nyelvben az aritmetikai műveletek: (+, -, *, /) mellett megkülönbözteti az // egészosztást és a % modulo operátort, ahol a maradék lehet negatív. A hatványozás jele a **, például 5**3 == 125 és 9**0.5 == 3.0. A @ a mátrixszorzás jele. Mindezek az operátorok a matematikában megszokott módon működnek: a preferencia szabályok érvényesülnek, az operátorok infixek. Van unáris + és operátor is, előjelek számára. A kacsa típusozás lehetővé teszi, hogy implicit konverzióval lehessen például összeszorozni egy egész és egy komplex számot explicit konverzió nélkül.

Az osztás viselkedését a 3.0 verziótól kezdve változtatták meg. Python terminológiával a / jeles osztás true division, míg a // jeles floor division. A korábbi verziókban a / jeles osztást classic divisionnek nevezték.Így 7//3 == 2, -7//3 == -3, 7.5//3 == 2.0 és -7.5//3 == -3.0. A Python 2.7 számára is elérhető ez a viselkedés: from __future__ import division kapcsolja be.

Mindig az alsó egészrészt venni következetességet jelent. Így mindig teljesül, hogy (a + b)//b == a//b + 1; és b*(a//b) + a%b == a, függetlenül a előjelétől. Azonban ez azt is jelenti, hogy a%b eredménye a [0, b) intervallumba esik, ha b pozitív, de a (b, 0] intervallumba, ha b negatív.

Lebegőpontos számokat a round kerekít a legközelebbi egészhez; a kapott érték azonban lebegőpontos ábrázolású marad. Kétség esetén párosra kerekít: round(1.5) és round(2.5) értéke is 2.0. A 3.0 előtti verziók az iskolai matematikából ismert módszert használták, amiben a pontosan a középre eső érték már nagynak számított: round(0.5) is 1.0 volt, és round(-0.5) eredménye −1.0 volt.

Az összehasonlítások kiértékelése is illeszkedik a matematikában szokásoshoz: a < b < c azt jelenti, hogy először elvégzi az a < b összehasonlítást, majd a b < c összehasonlítást, és az egész összehasonlítást az és művelettel kapja meg. C típusú nyelvekben az első összehasonlítás eredménye 0 vagy 1, és ezt hasonlítja össze c-vel.

Az egészaritmetika tetszőleges pontosságú. A decimal modul decimális lebegőpontos számokat biztosít, meghatározható pontossággal és különböző kerekítési módokkal. A fractions modul tetszőleges pontossággal kezel tört alakú racionális számokat.

A Python kiterjedt matematikai képességeit a függetlenül fejlesztett NumPy könyvtár bővíti. A Pythont ezzel kibővítve használják tudományos szkriptnyelvként numerikus adatfeldolgozásra és manipulációra.

7. Utasítások és vezérlő szerkezetek

A Python utasítások közé tartoznak:

  • az értékadás, egy = jellel
  • az if utasítás, ami feltételesen végrehajtandó blokkot jelent. Kiegészülhet else és elif ágakkal, melyekből az else az utolsó.
  • a for utasítás, ami egy iterálható objektumon halad át. Az iterálható objektum egyes elemei a ciklusmagban elérhetők egy változón keresztül.
  • a while utasítás, ami addig hajtja végre a ciklusmagot, amíg a feltétele igaz
  • a for és a while utasításokat else ág egészítheti ki, ami akkor hajtódik végre, ha a ciklus rendben lefutott, és nem break, return vagy kivétel szakította meg.
  • a try utasítás, ami lehetővé teszi, hogy a blokkban keletkező kivételt elfogjanak egy except utasítás blokkjában, vagy a 3.11-es verziótól kezdve az except* blokkban, mellyel kivételcsoportok kaphatók el. Azt is biztosítja, hogy lefusson egy finally kód is, azzal a céllal, hogy rendet rakjon, értve ez alatt az erőforrások elengedését.
  • a raise utasítás, amivel kivételek dobhatók, vagy elkapás után újradobhatók.
  • a break utasítás, amivel ki lehet lépni egy ciklusból
  • a continue utasítás, ami abbahagy az aktuális iterációt, és újat kezd
  • a pass utasítás, ami nem jelent semmit. Arra való, hogy üres blokkokat jelezzen.
  • a class utasítás, amivel osztály definiálható
  • a def utasítás, amivel függvény vagy metódus definiálható
  • a with utasítás, amivel környezetmenedzser blokkot definiál, az erőforrások egyszerűbb megszerzéséhez és felszabadításához. Technikailag ez magában foglalja a szükséges zár megszerzését és elengedését, vagy egy fájl megnyitását és bezárását a szokásos try/finally idióma helyett
  • a del utasítás, amivel változók törölhetők
  • a yield utasítás, mellyel generátorfüggvények több értéket is visszaadhatnak. Korutinok létrehozására használhatók.
  • a return utasítás, amivel függvény visszatérési értéke adható meg
  • az assert utasítás, amivel feltételek teljesülése ellenőrizhető
  • az import utasítás, amivel modulok importálhatók, ezzel hozzáférve azok tartalmához. A modulok meghatározhatják, hogy mely elemek importálhatók belőlük.
  • a mintaillesztés formája match … case … if.

Az értékadás (=) összekapcsol egy referenciát egy külön, dinamikusan allokált objektummal. Bármikor bármely változó hozzákapcsolható bármely objektumhoz. A változónevek általános referenciatartók meghatározott referenciatípus nélkül – viszont az objektumok, melyekre hivatkoznak, típusosak. Ezt úgy nevezik, hogy a nyelv dinamikusan típusos, szemben a statikusan típusos nyelvekkel, ahol a változónevek csak bizonyos típusú változókra hivatkozhatnak.

A Python nem támogatja a végrekurziót vagy az első osztályú kontinuációkat, és ezek Van Rossum szerint nincsenek is tervben. A generátorok kiterjesztésével azonban megvalósítható a korutinszerű viselkedés. A 2.5-ös verzió előtt a generátorok lusta iterátorok voltak; az adatok a generátor hívása után csak egy irányba mozogtak, a generátorból kifelé. Azóta a generátor hívása után is lehet vele kommunikálni, és a 3.3-tól kezdve több stack szinten át is lehet velük kommunikálni.

8. Kifejezések

Néhány Python kifejezés hasonlít C és Java kifejezésekhez, mások azonban különböznek:

  • Az összeadás, kivonás és szorzás ugyanúgy viselkedik, viszont az osztás különbözik. Pythonban kétféle osztás van: a / jel lebegőpontos osztást, míg a // egészosztást jelent. A hatványozás jele **.
  • A @ egy infix operátor, melyet a NumPy és más könyvtárak mátrixszorzásra használnak.
  • A := rozmár operátor (walrus operator) a 3.8-as verziótól kezdve használható. Egy nagyobb kifejezés részeként ad értéket változóknak.
  • Az == jel érték szerint hasonlít össze, szemben a Javával, ahol a primitív típusokat hasonlítja össze érték szerint, az objektumokat pedig referencia szerint. A referencia szerinti összehasonlításra az is operátor szolgál. Az összehasonlítások láncolhatók is, a <= b <= c.
  • A Python logikai operátorok and, or, és not ahelyett, hogy a &&, ||, ! szimbólumokat használná, mint a C és a Java.
  • Létezik a list comprehension típusú kifejezés, illetve az általánosabb generátor kifejezés.
  • A névtelen függvények lambda kifejezésekkel írhatók le, azonban a törzs csak egyetlen kifejezésből állhat.
  • A feltételes operátor formája x if c else y, ami különbözik a számos nyelvben elérhető c ? x : y formától.
  • A Python különbséget tesz listák és tuple-k között. A listák írásmódja [1, 2, 3], változtathatók, így nem lehetnek szótárak kulcsai. A szótárak kulcsainak megváltoztathatatlansága a konzisztencia megőrzését segíti. A tuple-k írásmódja (1, 2, 3), nem változtathatók, és lehetnek kulcsok szótárban. A tuple-kon végzett műveletek nem a tuple-t változtatják meg, hanem új tuple-t hoznak létre, így a + operátor is új tuple-t hoz létre. Ha a t tuple eredetileg (1, 2, 3), akkor az t = t + (4, 5) értékadás úgy értékelődik ki, hogy először a t + (4, 5) számítás végződik el, melynek eredménye (1, 2, 3, 4, 5), és ez lesz értékül adva a t változónak. A tuple-t határoló zárójelek elhagyhatók, ha ez nem okoz félreértést.
  • Definiálhatók range-ek. A definiálás módja: range(vég); range(kezdet, vég); vagy range(kezdet, vég, lépés). Valójában csak a vég paraméter kötelező. A kezdet alapértelmezetten nulla, a lépés egy.
  • A listák, tuple-ök és range-ek szekvenciának számítanak. A szekvenciának vannak további típusai is.
  • A szekvenciák és más adatszerkezetek kicsomagolása támogatott. Ami azt jelenti, hogy több változó kaphat értéket egy adatszerkezetből. Például fruits = ["apple", "banana", "cherry"]; x, y, z = fruits Valójában, változó helyett szerepelhet más is, aminek értéket lehet adni, az adatszerkezet pedig iterálható objektum.
  • A % string formátum operátor a printf formázó operátorához hasonlóan működik. Objetumokat implicit konvertál stringgé. Például a "spam=%s eggs=%d" % ("blah", 2) kiértékelése "spam=blah eggs=2". A 2.6-os verziótól kezdve ezt a string format() metódusa egészíti ki. Például "spam={0} eggs={1}".format("blah", 2). A 3.6-os verziótól kezdve rendelkezésre állnak az "f-string"ek is: spam = "blah"; eggs = 2; f'spam={spam} eggs={eggs}'.
  • A stringek összefűzését + jelöli, ez ugyanaz az operátor, amivel számokat adhatunk össze, például "spam" + "eggs" eredménye "spameggs". Ha a stringek számokat tartalmaznak, akkor nem történik konverzió, hanem stringszerűen adódnak össze, például "2" + "2" eredménye "22". A stringek létrehozásuk után nem módosíthatók; a műveletek új stringeket hoznak létre. A stringek ugyanúgy iterálhatók, mint a listák, tuple-ök, szótárak és range-ek.
  • A nyelvben többféle string literál is van:

    • Egy vagy dupla idézőjeles stringek: Több más nyelvtől, mint Unix héjak vagy a Perl eltérően ezek a stringek ugyanúgy működnek. Escape-elni a karakterrel lehet. A string interpoláció a 3.6-os verziótól kezdve érhető el, formázott string literálokként.
    • Háromidézőjeles, többsoros stringek. Az idézőjelek lehetnek egyszeres vagy dupla idézőjelek, de nem keverhetők. A héjak, Perl vagy Ruby nyelvekben szokásos helyszíni dokumentumok szerepét tölthetik be.
    • Nyers stringek, melyeket r prefix jelöl. Ezek a stringek nem értelmeződnek. Hasznosak olyankor, amikor gyakoriak a literális karakterek, mint Windows elérési útvonalak vagy reguláris kifejezések.
  • A listák más nyelvek tömbjeihez hasonlóan szeletelhetők. Az egyes elemek hivatkozhatók, mint a[key], részlisták úgy, mint a[kezdet:vég] vagy a[kezdet:vég:lépés]. Az indexelés nullától kezdődik; a negatív indexek a végtől számítódnak visszafelé. Így az utolsó előtti elem a -1-edik, az az előtti a -2-edik, és így tovább. A szeletelésben a résztömbök a kezdet indextől kezdve a vég indexig, de azt már bele nem véve számítódnak. A harmadik paraméter lehetővé teszi elemek átugrását vagy megfordítását. A szélső elemekre vonatkozó kezdet és vég paraméter elhagyható: a[:] az eredeti tömb másolatát adja.

  • A halmazok (set) képesek akárhány elemet befogadni, de egy elemet csak egyszer tartalmazhatnak. Rendelkezésre állnak a matematikából ismert halmazműveletek, mint az unió, a metszet és a differencia. A halmazból is van nem módosítható, ez a frozenset.

A Python kikényszeríti a kifejezések és az utasítások megkülönböztetését, szemben a Ruby, a Common Lisp és a Scheme nyelvekkel. Ez bizonyos funkciók megduplázásához vezet:

  • for ciklusok és list comprehension-ök
  • if blokkok és feltételes kifejezések
  • az exec() és az eval() beépített függvények; az előbbi utasítások, az utóbbi kifejezések számára

Az utasítások nem lehetnek kifejezések részei, így a lista és más comprehensionök, illegve a lambda kifejezések nem tartalmazhatnak utasításokat, mivel kifejezések. Például egy értékadás sem lehet egy feltételes kifejezés része. Ennek az az előnye, hogy elkerülhető a klasszikus C programozási hiba, hogy összehasonlítás helyett véletlenül értékadást írnak. Ez C-ben értelmes dolog, habár többnyire tévedés, míg Pythonban szintaktikai hiba.

9. Kivételkezelés

A kivételkezelés a try kulcsszóval történik. Például így:

try:
    f()
except (NameError, TypeError):
    print('Az f függvény végrehajtása során NameError vagy TypeError lépett fel.')
except Exception:
    print('Nem várt kivétel lépett fel.')
else:
    print('Semmilyen kivétel nem lépett fel.')
finally:
    print('Ez a mondat mindenképp kiíródik.')

Ha olyan kivétel lép fel a try blokkban, ami valamely except ágban szerepel, akkor a vezérlés az illető except ágnak adódik át. Egy except ág több kivételtípust is kezelhet, az egyes kivételtípusokat vesszővel elválasztva lehet megadni.

Az except ág lefutása után a try blokk utáni részen folytatódik a program. Ha nem lép fel semmilyen kivétel, akkor a vezérlés az else ágra kerül a lefutás után, ha az létezik. Mindig csak egy except ág fut le. Ha az utolsó except ág nem ad meg kivételtípust, akkor az kezeli az összes olyan kivételt, amit a megelőző ágak nem kezeltek. Végül szerepelhet egy opcionális finally blokk, ami mindenképpen lefut.

Ha nincs megfelelő except ág, akkor továbbadódik a kivétel a tartalmazó blokknak. Az except ágakban fellépő kivételek szintén a tartalmazó blokknak adódnak át. Ha egyáltalán nincs try blokk, például egy függvényben, akkor minden kivétel a tartalmazó blokknak adódik át.

def hibas_fuggveny():
    x=1/0
try:
    hibas_fuggveny()
except ZeroDivisionError as ex:
    print('Nullával osztás.', ex)

A nyelv tartalmaz beépített kivételeket, de a lehetőség van saját kivételeket definiálására is. A kivételek paraméterezhetőek, típusuktól függően más és más paraméterük lehet. Kivétel kiváltására a raise kulcsszó alkalmazható:

raise NameError('Hello')

A Python egyik elve szerint a kivételeket keletkezésükhöz közel kell kezelni. Ez a megközelítés hasznos robusztus inputfeltételek megalkotásához:

while True:
    num = input("Eine ganze Zahl eingeben: ")

    try:
        num = int(num)
    except ValueError:
        print("Eine _Zahl_, bitte!")
    else:
        break

Ez a programszakasz egy egész számot kér a felhasználótól, egészen addig, amíg az egy olyan stringet ad meg, ami egész számmá konvertálható. A kivételkezeléssel egy futás idejű kivételt kerül el, és helyette a felhasználó által is érthető üzenetet ír ki.

A kivételkezelés szálbiztos, és a kivételek könnyen továbbíthatók a program tetszőleges szintjére. A dinamikus erőforrásokhoz való hozzáférést is leegyszerűsítik, mivel így elkerülhetők a race conditionök, hogy esetleg az elavult információk alapján többen ugyanahhoz az erőforráshoz akarnak hozzáférni.

A Python kiterjedten használja a kivételkezelést hibafeltételek kezelésére. Ez olyan mélyen be van ágyazva, hogy még a szintaktikai hibák is elkaphatók és kezelhetők. Elkapható és kezelhető (except KeyboardInterrupt: …) a megszakítás jel is (interrupt signal, SIGINT, Ctrl + C).

10. Metódusok

A metódusok osztályban definiált függvények. Az instance.method(argument) hívásmód szintaktikus cukor a Class.method(instance, argument) helyett. Pythonban az osztályokban a metódusokban kötelező a self paraméter, ami az osztálypéldányra hivatkozik. A self paraméter akkor is kötelező, ha a metódusnak nem kell hozzáférnie a példányhoz. A legtöbb objektumorientált nyelvben a this implicit adódik át (lásd C++, Java, Objective-C, Ruby). Az operátorok (aritmetikai, összehasonlítás, hossz, konverzió) felüldefiniálhatók meghatározott nevű metódusokkal.

11. Osztályok, öröklődés

A Python osztálymechanizmusának tervezésénél a szempont az volt, hogy minimális szintaktikai és szemantikai újdonságokat vezessenek be. C++ és a Modula-3 osztálymechanizmusának a keveréke. Többszörös öröklődésre is lehetőséget ad, a származtatott osztály átdefiniálhatja az ősosztálya(inak) metódusait, egy metódus hívhatja az ősosztály metódusát ugyanazon a néven. Az objektumok tartalmazhatnak nem publikusnak szánt adatokat, azonban maga a nyelv semmi biztosítékot nem nyújt arra, hogy hívó fél is valóban így fogja kezelni. A tagváltozó vagy tagfüggvényt kezdhetjük egy aláhúzással, ezzel jelezvén, hogy ezt nem publikusnak szánjuk (gyakorlatilag a protected, package-private vagy privát láthatóságot szánjuk az adott tagnak).

A protected tagváltozót vagy tagfüggvényt egy aláhúzással, a private tagváltozót vagy tagfüggvényt két aláhúzással jelöljük. Ezeket ugyan a hívó fél nem érheti el hagyományos módon, de elérheti mangled name alapján: OsztalyNév_tag_név (protected) vagy OsztalyNév__tag_név (private) néven. A rendszer speciális jelentést tulajdonít azoknak a tagoknak, melyek neve nemcsak hogy két aláhúzással kezdődik, de azzal is végződik. Ha valamit el akarunk rejteni a külvilág elől, akkor tegyük egy függvény belsejébe, vagy egy modulba, mivel egy modul eldöntheti, hogy mit exportál.

class MyObject(object):
    def __init__(self, name):
        self._name = name   # Jelezzük, hogy ez egy protected vagy package-private adat
                            # nem szeretnénk, ha direktben használná bárki
                            # kivéve a csomagot, amiben van/leszármazott osztályokat

myobj = MyObject("Ez a nevem")
print(myobj._name) # De ez csak egy jelzés, kívülről ugyanúgy elérhető, mint bármely más adat

Különbségek a C++-hoz képest, hogy az osztály- és objektumváltozók publikusak (kivéve a dupla aláhúzással kezdődőeket, amik egy speciális mechanizmusnak köszönhetően megóvhatják a kódunkat például az öröklődéskor előforduló névfelüldefiniálásoktól), és minden tagfüggvény virtuális.

A Python a szokásos értelemben nem használ konstruktor és destruktor függvényeket, de a nem kötelezően definiálandó, speciális __init__ és __del__ tagfüggvényeket a rendszer az objektumpéldány létrehozásakor, illetve az objektum explicit törlésekor (del utasítás) vagy amikor a szemétgyűjtő (garbage collector) felszabadítja a tárhelyet, automatikusan meghívja. Az __init__-et nagyon gyakran használják az tagváltozók kezdeti értékadására:

class MyObject(object):
    def __init__(self, name):
        self.name = name

myobj = MyObject("Ez a nevem")
print(myobj.name) # Kiírja, hogy "Ez a nevem"

Az osztályok maguk is objektumok – valójában a Pythonban minden adattípus objektum. A 2.2-es verziótól kezdve a beépített típusokat is bővítheti a felhasználó. Minden operátor felüldefiniálható speciális nevű tagfüggvényekben. (Például az összeadás operátor (+) a __add__, __radd__, __ladd__ segítségével, a szorzás operátor (*) a __mul__, __rmul__, __lmul__ segítségével, stb.)

Ugyanarra az objektumra több néven is lehet hivatkozni, objektumok esetében értékadás alapértelmezés szerint referenciát (hivatkozást) jelent, nem új objektumpéldány létrehozását.

Osztálydefiníció:

class ClassName(object):
    <statement-1>
    ...
    <statement-N>

#Például:
class MyClass(object):
    "Egy egyszerű példa osztály"
    i = 42
    def f(self):
        return 'hello world!'

Az osztálynak mielőtt hatása lenne, a vezérlésnek rá kell futnia az osztálydefinícióra, így akár egy if-ágban is lehet osztálydefiníció. Az osztály-objektum az osztálydefiníció végén automatikusan létrejön. Példányosítani az osztály nevével, valamint a paraméterlista megadásával tudunk. Üres paraméterlistát is jeleznünk kell (nem úgy, mint C++-ban), egy üres zárójel-párral, különben az osztály referenciáját másoljuk egy változóba. (pl.: x = MyClass()). Az objektumok tagváltozóit nem az osztálydefinícióban deklaráljuk, hanem az objektum inicializálására használt __init__ metódusban, hiszen ezek így lokálisan, adott objektumpéldányra lesznek érvényesek, míg az osztálydefinícióban deklarált változók az egész osztályra (így bármely objektumára is) érvényesek lesznek.

Azonban azt is megtehetjük, hogy egy adat attribútumot később deklarálunk, mely a használatkor jön létre. Példa:

class MyObject(object):
    i = 42  # összes objektumpéldányra érvényes változó
            # (hasonlít a hagyományos statikus változókhoz, de nem teljesen ugyanaz a működése)
    def __init__(self):
        self.counter = 0  # objektum inicializáláskor létrehozunk egy tagváltozót

ctr = MyObject()
ctr.counter += 10  # módosítjuk a tagváltozó értékét

print(ctr.counter)  # 10
print(ctr.i)  # 42

ctr.anotherCounter = 0  # deklarálunk egy új adattagot az adott objektumba
print(ctr.anotherCounter)  # 0

MyObject.i = 100  # Az osztályban lévő i változót módosítjuk
anotherOne = MyObject()  # Létrehozunk egy másik objektumpéldányt
print(anotherOne.i)  # 100
print(ctr.i)  # 100
# print(ctr.anotherCounter)  # ebben az objektumban nincs ilyen tagváltozó
A del utasítással megszüntethetünk bármilyen változót, akár objektum-tagváltozót is.

class MyObject(object):
    def __init__(self):
        self.counter = 0

ctr = MyObject()
ctr.counter += 10
print(ctr.counter)  # 10
del ctr.counter
# print(ctr.counter) # itt már nincs ilyen tagváltozónk

Egy másik példa

def MyClass(object):
    i = 42
    def f(self):
        return 'hello world!'

x = MyClass()
x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print(x.counter)
del x.counter

Ez a kis példa 16-ot ír ki (nem a legegyszerűbb módon), és semmilyen nyoma nem marad az osztályban, hiszen a del utasítással töröltük a létrehozott counter nevű változót.

Ügyeljünk rá, hogy x.f nem ugyanaz, mint MyClass.f, mivel az első az hivatkozás az adott objektum egy tagfüggvényére (metódus típusú objektumra való hivatkozás), amely hivatkozás futás közben megváltozhat (tehát például más metódusra mutat), míg a MyClass.f a f függvény prototípusa, ezáltal függvény típusú objektumra való hivatkozás! x.f egy metódus objektum, nem függvényobjektum. x.f() – ki fogja írni: hello world. Ugyanis az objektum, mint első argumentum átadódik a függvénynek, azaz x.f() ekvivalens MyClass.f(x) -szel.

További megjegyzések:

  • az adat attribútumok felülírják az ugyanolyan nevű metódus attribútumot! Ezért célszerű valamilyen névkonvencióval kizárni az ilyen lehetőséget.
  • nincs lehetőség az adatelrejtésre – az adat attribútumokat éppúgy elérik a metódusok, mint az objektum kliensei.
  • az előbbi lehetővé teszi, hogy kliensek elrontsák az invariánst, ha meglévő adat attribútumot írnak felül. Ezt a programozónak kell megoldania, mivel a nyelv nem nyújt rá lehetőséget.
  • ha létezik egy __init__() metódusa az osztálynak, akkor példányosításkor az objektum létrehozása után meghívódik, átadva a példányosításkor esetleg megadott paramétereket:
class Alma(object):
    def __init__(self, szin, iz):
        self.szin = szin
        self.iz = iz

x = Alma("piros", "savanyu")

A Python lehetőséget nyújt a többszörös öröklődésre, melynek szintaxisa az alábbiak szerint néz ki:

class DerivedClassName([modulename.]Base1[,[[modulename.]Base2,]):
    <statement-1>
    ...
    <statement-N>

Ha egy hivatkozást nem talál az aktuális osztályban, akkor Base1-ben keresi, ha Base1-ben sincs, akkor Base1 őseiben. Ezután ha még mindig nem találta, akkor Base2-ben kezdi el keresni, és így tovább. Rekord vagy struct-szerű objektumok létrehozására is van lehetőség, a már ismertetettek szerint, például egy üres osztály deklarálásával, majd az üres osztály egy példányát feltölthetjük:

class Dolgozo:
pass        # ez egy üres osztálydefiníció

John = Dolgozo()
John.nev = 'John Cosinus'
John.osztaly = 'Matematikai reszleg'
John.fizetes = 42000

Azonban, ha ilyen céljaink vannak, sokkal inkább ajánlott a szótár (asszociatív tömb) használata, hogy feleslegesen ne terheljük az interpretert ilyen dummy osztályok, majd abból keletkező objektumok létrehozásával:

dolgozok = list()

John = {'nev': 'John Cosinus',
      'osztaly': 'Matematikai részleg',
      'fizetes': 42000}
dolgozok.append(John)

Jason = dict()
Jason['nev'] = 'Jason Cosinus'
Jason['osztaly'] = 'Matematikai reszleg'
Jason['fizetes'] = 42000
dolgozok.append(Jason)

A kivételek korábban lehettek egyszerű string objektumok, azonban Python 3-ban csak és kizárólag a "BaseException" osztály, vagy leszármazottjának egy példánya lehet. Forma: raise instance.

12. Példa kódok GitHub-on

:octicons-file-code-24: Példakód