A Python nyelv elemei

Kategória: Python.

Változók

Változók minden programozási nyelvben vannak, így a Pythonban is. Léteznek erősen és gyengén típusos programozási nyelvek. Az erősen típusos nyelvekben kötelező megadni a változó típusát, és az automatikus konverzió nulltól a korlátozottig terjed, míg a gyengén típusos nyelvek esetében általában nem kell, sőt, sok esetben nem is lehet megadnia típust, és a konverzió nagyrészt automatikus. Úgy látom, a Python valahol a kettő között helyezkedik el.

Típusok

A típust nem kötelező megadni, a rendszer megpróbálja kitalálni:

a = 5
s = "alma"

Opcionálisan megadhatjuk, a változónév után írva, kettősponttal elválasztva:

a:int = 5
s:str = "alma"

Automatikus konverzió nincs. Pl. a következő hibás:

n = "3"
print(n+2)

Konvertálni a típus megadásával, a konvertálandó változó vagy érték zárójelbe írásával lehet; ennek a szintaxisa tehát függvényhívás:

n = "3"
print(int(n)+2) # 5

Ill.:

n = "3"
print(n+str(2)) # 32

Itt az a, a b és a c rendre felveszik a 3, 4 és 5 értékeket.

A változónevek adásánál a Pythonban hasonlóak a szabályok, mint a legtöbb programozási nyelvben: alfanumerikus karaktereket és néhány speciális karaktert (pl. aláhúzás) tartalmazhat, és nem kezdődhet számmal. A tipikus Python konvenció az ún. snake case, pl. number_of_pythons.

Egy változó típusát a type() függvénnyel kérhetjük le:

i = 5
print(type(i)) # <class 'int'>

Változót del-lel tudunk törölni:

i = 5
del i
print(i) # ... NameError: name 'i' is not defined

Az alábbi alfejezetekben a Python típusait és a leggyakoribb műveleteket vesszük át.

Szám típusok

A Pythonban - az erősen típusos programozási nyelvektől eltérően - nincs lehetőség sem a méretre sem az előjelre vonatkozó típust megadni. Az alábbi 3 szám típus létezik:

Egész

int

a = 5
a:int = 5

A tapasztalatom szerint nincs korlátozva a mérete. A számokat megadhatjuk a más programozási nyelvekben megszokott módokon.

Decimálisan, hexadecimálisan, binárisan, oktálisan:

d = 255
h = 0xFF
b = 0b11111111
o = 0o377

A nagy számok megadásánál lehetőség van az ezreseket elválasztva aláhúzást írni:

m = 1_000_000

Lebegőpontos

float

f = 4.5
f:float = 4.5

Lehetőség van exponenciális alakban is megadni a számot:

avogadro = 6.022e23

Komplex

complex

Nem szokványos módon a Python alapból tartalmazza a komplex számtípust. Ugyancsak nem szokványos módon az imaginárius részt nem i-vel, hanem j-vel kell írni:

c = 4 + 3j
c:complex = 4 + 3j

Műveletek

A szokásos aritmetikai szám műveleteket tudjuk végrehajtani:

7 + 2  # összeadás: 9
7 - 2  # kivonás: 5
7 * 2  # szorzás: 14
7 / 2  # osztás a valós számok halmazán: 3.5
7 // 2 # maradékos osztás eredménye: 3
7 % 2  # maradékos osztás maradéka: 1
7 ** 2 # hatványozás: 49

Bit műveletek:

7 << 2 # bitek mozgatása balra: 28
7 >> 2 # bitek mozgatása jobbra: 1
b = 0b1010 # decimálisan 10, mert 8+2=10
b & 0b1100 # bitenkénti logikai ÉS; az eredmény binárisan 1000, decimálisan 8
b | 0b1100 # bitenkénti logikai VAGY; az eredmény binárisan 1110, decimálisan 14
b ^ 0b1100 # bitenkénti logikai kizáró VAGY (XOR); az eredmény binárisan 0110, decimálisan 6
~b         # bitenkénti tagadás. Mivel az eredmény binárisan 111…110011 lesz, ez decimálisan -11.

Értékadásnál használhatjuk a += és hasonló operátort:

a = 5
a += 3
print(a) # 8

A többi művelettel is működik: -=, //=, |= stb.

Matematika

Néhány alapművelet alapból használható:

min([3, 2, 4]) # 2
max([3, 2, 4]) # 4
pow(3, 4)      # 81

A math modul importálásával pedig lehetőségünk van a leggyakoribb matematikai műveletek (trigonometria, logaritmus stb.) végrehajtására. Ugyanitt a legfontosabb konstansokat is megtaláljuk.

import math
 
math.sqrt(2)        # 1.4142135623730951
math.cos(math.pi/3) # 0.5000000000000001

Véletlen szám generálás

A véletlen szám generátorhoz a random modult hazsnálhatjuk:

import random

Véletlen egészet a random.randint() függvénnyel tudunk generálni, melyhez meg kell adnunk, az intervallum elejét és végét. Pl. dobókocka szimulálás:

random.randint(1, 6)

Egy listában elemeket a random.shuffle() függvénnyel tudunk összekeverni:

fruits = ['apple', 'banana', 'lemon', 'cherry', 'watermelon']
random.shuffle(fruits)
print(fruits) # ['lemon', 'banana', 'apple', 'cherry', 'watermelon']

Listából véletlenszerűen elemeket a random.sample() függvény segítségével tudunk kiválasztani:

random.sample(fruits, 3) # ['banana', 'lemon', 'apple']

Még számos egyéb lehetőség is van, melyről pl. ezen az oldalon tájékozhatunk: https://www.w3schools.com/python/module_random.asp.

Szakasz

range

Megadása: range() függvénnyel.

r = range(6)
r:range = range(6)

A változó típusa range:

print(r) # range(0, 6)
print(type(r)) # <class 'range'>

Tipikusan végig lehet rajta lépkedni:

for e in r:
    print(e)

Ez kiírja a számokat 0-tól 5-ig.

A range() függvénynek az alábbi 3 formája használatos:

  • 1 paraméter: 0-tól a megadott szám -1-ig tart, pl. a range(6) a 0, 1, 2, 3, 4, 5 számokat jelenti.
  • 2 paraméter: az első számtól a második szám mínusz 1-ig terjed, pl. a range(2, 6) a 2, 3, 4, 5 számokat jelenti. Az első paraméternek értelemszerűen kisebbnek kell lennie mint a másik, bár ellenkező esetben a rendszer nem jelez hibát, csak az eredmény lesz üres.
  • 3 paraméter: a harmadik paraméter a lépést adja meg. Ez alapértelmezésben 1. Pl. a range(2, 8, 2) eredménye 2, 4, 6.

Logikai

bool

Igaz és hamis értékek: True ill. False. (Ez tehát eltár a más programozási nyelvekben megszokott kisbetűs true és false értékektől.)

b = True
b:bool = True

Az egyéb programozási nyelvekben is megszokott 3 logikai alapműveletet használhatjuk, a következőképpen:

True and False # logikai ÉS: False
True or False  # logikai VAGY: True
not True       # logikai tagadás: False

Az összehasonlító operátorok eredménye is logikai:

7 == 2 # False
7 != 2 # True
7 < 2  # False
7 > 2  # True
7 <= 2 # False
7 >= 2 # True

Hasonlóan más programozási nyelvekhez, a Python is különbséget tesz érték és memóriacím szerinti összehasonlítás között. Az == érték szerint hasonlít (ez eltér a Java nyelvben alkalmazottól, ahol az == pont a memóriacím szerinti összehasonlítás). A Pythonban a memóriacím összehasonlítása az is, ill. a párja az is not. Példaként egy picit előre kell szaladnunk, és vegyük a listát.

a = ["apple", "banana", "orange"] # ez egy 3 elemű lista
b = ["apple", "banana", "orange"] # ez is egy 3 elemű lista, ugyanazokkal az elemekkel, de más memóriacímen
c = a                             # a c is lista, aminek a memóriacíme ugyanoda mutat, mint az a
a == b                            # True, mivel érték szerint hasonlít 
a is b                            # False, mivel memóriacím szerint hasonlít
a is not b                        # True, mert az előző ellentettje
a is c                            # True, mert memóriacím szerint hasonlít, és az a és a c ugyanarra a memóriacímre mutat

Végül logikai eredményt ad még az in ill. not in, ami a gyűjtemény típusok esetén azt vizsgálja, hogy egy elem része-e:

"banana" in ["apple", "banana", "orange"]    # True
"peach" in ["apple", "banana", "orange"]     # False
"peach" not in ["apple", "banana", "orange"] # True

String

str

Példa:

s = "hello"
s:str = "hello"

Létrehozás

String létrehozásánál használhatunk egyszeres és kétszeres idézőjelet is:

s = "hello"
s = 'hello'

Három idézőjellel többsoros stringet is létrehozhatunk (három aposztroffal is működik):

+s = """Hello,
world!"""

Ha nem adjuk értékül, akkor a Python figyelmen kívül hagyja, így ennek segítségével gyakorlatilag többsoros megjegyzéseket hozhatunk létre.

Formázás

Ha az idézőjelek elé f karaktert írunk, akkor kapcsos zárójelek közé írhatunk változónevet:

name = "Csaba"
print(f"Hello, {name}!") # Hello, Csaba!

Stringeket egymáshoz fűzni a + operátorral lehet:

name = "Csaba"
print("Hello, " + name + "!") # Hello, Csaba!

Karakterek

A stringeket a tömböknél megszokott módon meg is címezhetjük; ez esetben az adott sorszámú karaktert kapjuk eredményül (0-tól indítva a sorszámozást).

name = "Csaba"
print(name[3])   # b

A string nem megváltoztatható, tehát a címzéssel értéket nem adhatunk.

A karaktereket for ciklussal végig is tudunk lépkedni:

name = "Csaba"
for c in name:
    print(c)

melynek eredménye:

C
s
a
b
a

Feldarabolás

Kettősponttal (:) darabolhatjuk a stringet; elválasztva a kezdő és a vég karaktert is megcímezhetjük. A címzésnél a vég karakter indexe eggyel a tényleges után mutat. A azső és a felső index is opcionális:

name = "Csaba"
print(name[1:4]) # sab
print(name[:3])  # Csa
print(name[2:])  # aba
print(name[:])   # Csaba

Speciális karaktereket a visszaper jellel (\) tudunk beszúrni. Ezt escape szekvenciának hívjuk. A más programozási nyelvekben is szokásos escape szekvenciákat használhatjuk, pl.

print("Hello,\n\"World\"!")

Ennek eredménye:

Hello,
"World"!

További string műveletek

A leggyakoribb string műveleteket az alábbi táblázat foglalja össze. Vegyük figyelembe a következőket:

  • A példákban az egyszerűség érdekében adott stringek vannak. Természetesen használhatnánk string változót is, pl. s = "Csaba", majd len(s).
  • A műveletek nem változtatják meg magát a stringet, hanem az új eredményt visszaadják. Tehát pl. s = "Csaba", majd u = s.upper() hívás hatására az s ez marad "Csaba", és az u lesz "CSABA".
Művelet Függvény Példa Eredmény Magyarázat
hossz len(str) len("Csaba") 5 A paraméterül kapott string hosszát adja vissza.
rész ellenőrzés str1 in str2 "world" in "Hello, world!" True Megadja, hogy egy string része-e egy másiknak. Párja: str1 not in str2. Hasonló függvények:
- str1.find(str2): visszaadja az adott string első előfordulását
- str1.index(str2): ugyanaz, mint az előző, azzal a különbséggel, hogy ha nem tartalmazza a máik stringet, ez ValueError hibát dob, míg az előbbi eredménye -1
- str1.startswith(str2): adott stringgel kezdődik-e
- str1.endswith(str2): adott stringgel végződik-e
- str1.count(str2): megszámolja, hogy hányszor fordul elő az egyik stringben a másik
szám ellenőrzés str.isnumeric() "123".isnumeric() true Megadja, hogy a string csak számokat tartalmaz-e. (A negatív előjel és a tizedespont nem számít számnak.) Hasonló műveletek:
- str.isdigit(): csak számokat tartalmaz-e. Kicsit bővebb, mint az isnumeric(), pl. a kitevőket (pl. ) is annak értékeli.
- str.isnumeric(): csak számokat tartalmaz-e. Kicsit bővebb, mint az isdigit() pl. a törteket (pl. ¾) is annak értékeli.
- str.isalpha(): csak betűket tartalmaz-e
- str.isalnum(): csak számokat és betűket tartalmaz-e
- str.isprintable(): csak nyomtatható karaktereket tartalmaz-e
- str.islower(): csak kisbetűket tartalmaz-e
- str.isupper(): csak nagybetűket tartalmaz-e
- str.istitle(): igaz-e az, hogy minden szó nagybetűvel kezdődik
- str.isidentifier(): a string alkalmas-e azonosítónak
- str.isspace(): csak "fehér" karaktereket tartalmaz-e (pl. szóköz, tabulátor, új sor)
formázás str.format(params) "My name is {}, and I am {} years old.".format("Csaba", 43)) "My name is Csaba, and I am 43 years old." Ez az f"…{…}…" módszer másik fajtája: amikor a zárójeleket üresen hagyjuk, és a paramétereket felsoroljuk.
középre zárás str.center(length) "Csaba.center(10)" " Csaba " Középre zárja az adott stringet; az eredmény a paraméterül átadott hossznyi lesz. Hasonló függvénye:
- str.ljust(length): balra zárt
- str.rjust(length): jobbra zárt
- str.zfill(length): olyan, mint a jobbra zárt, de szóközök helyett nullákkal tölti fel
szóközök törlése str.strip() " Hello, world! ".strip() "Hello, world!" Törli a string elején és végén található fehér karaktereket (pl. szóközöket). Hasonló függvények:
- str.lstrip(): csak a bal oldali fehér karaktereket törli
- str.rstrip(): csak a jobb oldali fehér karaktereket törli
nagybetűsítés str.upper() "Csaba".upper() "CSABA" Egy adott stringhen hajtjuk végre; az eredmény az adott string nagybetűs változata. Ékezetekkel is működik: az "áéíóöőúüű".upper() eredménye "ÁÉÍÓÖŐÚÜŰ". Hasonló függvények:
- str.lower(): kisbetűsítés
- str.casefold(): kisbetűsítés (nem latin karakterek esetén)
- str.swapcase(): a kisbetűkből nagybetűk, a nagybetűkből kisbetűk lesznek
- str.capitalize(): az első betűt nagybetűsíti
- str.title(): minden szó leső bejűték nagybetűsíti
csere str.replace(old, new) "I like spinach.".replace("spinach", "ice cream") "I like ice cream." Lecseréli az első paraméterként megadott összes előfordulást a második paraméterre.
Hasonló eredményt kapunk a maketrans() ill. a translate() mechanizmussal. A maketrans() függvénynek meg kell adnunk, hogy mely karaktereket mely karakterekre szeretnénk cserélni, ill. opcionálisan melyeket szeretnénk törölni, majd a translate() hívással ténylegesen végrehajtani a cserét. Pl.:
txt = "abcdabcd"
table = txt.maketrans("ab", "ef", "c")
txt.translate(table) # "efdefd"
felbontás str.split(delimiter) "apple, banana, peach".split(", ") ["apple", "banana", "peach"] A paraméterként megadott elválasztó mentén felbontja a stringet. Az eredmény egy lista. Hasonló függvények:
- str.rsplit(delimiter): ugyanaz, mint a split, de jobbról kezdi a felbontást
- str.partition(delimiter): adott határoló mentén osztja 3 részre
összefűzés delimiter.join(iterable) ", ".join(["apple", "banana", "peach"]) "apple, banana, peach" Összefűzi a karaktereket adott elválasztóval.

Reguláris kifejezések

A reguláris kifejezések használatához az re modult kell betölteni:

import re

Egy szövegben a re.findall() metódussal tudjuk megkeresni az összes előfordulást: az első paraméter az a reguláris kifejezés, amit keresünk, a második pedig amoiben keressük:

re.findall("a[a-c]a", "eca aba abc aca") # ['aba', 'aca']

Általában a szokásos reguláris kifejezési formákat használhatjuk (pl. [0-9]{1,} a nemnegatív egészekre illeszkedik), ami tartalmazhatja a leggyakoribb olyan karakter osztályokat is, amelyek visszaper jellel kezdődnek (pl.: \d: számjegy). Mivel a visszaper jelnek a stringekben általában egyéb jelentése is lehet (pl. \n: új sor), a string elé írt r karakterrel jelezhetjük, hogy a visszaper jelet hagyja eredeti formában, pl. r"\d{1,}".

Valamivel többet tud, de a használata is bonyolultabb a re.search() függvénynek: ez egy Match osztályt ad eredményül. A két paramétere ugyanaz, mint a findall() függvényé. Ennek az osztálynak a group() metódusa visszaadja az első találatot, míg a span() azt, hogy hol van:

re.search("a[a-c]a", "eca aba abc aca").group() # 'aba'
re.search("a[a-c]a", "eca aba abc aca").span()  # (4, 7)

A re.split() felbontja a stringet adott reguláris kifejezés mentén:

re.split(" ", "eca aba abc aca") # ['eca', 'aba', 'abc', 'aca']

A re.sub() lecseréli a találatokat valami másra, pl.:

re.sub(" ", "   ", "eca aba abc aca") # 'eca   aba   abc   aca'

Gyűjtemények

A Python néhány gyűjtemény típust már nyelv szinten támogat. Ezeket itt említés szintjén tekintjük át; további részleteket külön alfejezetben láthatunk.

Lista

list

Megadása: szögletes zárójelekkel: […].

fruits = ["apple", "orange", "banana", "pear", "mandarin"]
fruits:list = ["apple", "orange", "banana", "pear", "mandarin"]

Ellentétben más programozási nyelvekkel, a Pythonban nincs külön tömb (array); a lista és a tömb itt ugyanaz.

A listában az elemek sorrendje adott. A típusokat vegyíthetjük, bár ez nem célszerű.

Elem n-es

tuple

Megadása: kerek zárójelekkel: (…).

t = ("apple", "orange", "banana")
t:tuple = ("apple", "orange", "banana")

Tipikus felhasználása: ha egy függvény egyszerre több mindent, de fix mennyiséget szeretne visszaadni. Itt is adott az elemek sorrendje, és vegyíthetőek a típusok, ami jelen esetben tipikus. Ez nem megváltoztatható.

Halmaz

set, frozenset

Megadása: kapcsos zárójellel: {…}.

fruits = {"apple", "orange", "banana"}
fruits:set = {"apple", "orange", "banana"}

A frozenset nem megváltoztatható halmaz:

fruits = frozenset({"apple", "orange", "banana"})
fruits:frozenset = frozenset({"apple", "orange", "banana"})

Asszociatív tömb

dict

Más néven szótár. Megadása: kapcsos zárójellel, kulcsokat és az értékeket kettősponttal elválasztva: {…:…, …}.

fruits = {"apple": "alma", "banana": "banán", "orange": "narancs"}
fruits:dict = {"apple": "alma", "banana": "banán", "orange": "narancs"}

A szótár kulcs-érték párokat tartalmaz. Adott kulcsú elem értékének lekérdezése:

print(fruits["apple"])     # alma

Bináris típusok

Viszonylag ritkán használt típusok.

Bájt tömb

bytearray

Létrehozás:

ba = bytearray(b"alma")
print(ba) # bytearray(b'alma')
print(type(ba)) # <class 'bytearray'>

Figyeljük meg a b előtagot a string előtt.

Megadhatjuk csak a méretet, és egyesével be tudjuk állítani az értékeket:

ba = bytearray(4)
ba[0] = 97
ba[1] = 108
ba[2] = 109
ba[3] = 97
print(ba) # bytearray(b'alma')

Bájtok

bytes

Létrehozás:

by = bytes(b"alma")
print(by) # b'alma'
print(type(by)) # <class 'bytes'>

Lényeges különbség a bytearray és a bytes között az, hogy ez utóbbi nem módosítható.

Memória nézet

memoryview

Létrehozás:

ba = bytearray(b"alma")
mv = memoryview(ba)
print(mv) # <memory at 0x0000015AB8120400>
print(type(mv)) # <class 'memoryview'>

Ha megváltoztatjuk a tartalmát, akkor az eredeti memóriaterület változik:

mv[2] = 102
print(ba) # bytearray(b'alfa')

A bytes típusból is létrehozhatjuk, és ez esetben innen sem lehet módosítani.

Az igazi lényegét én magam sem értem; nem tudok olyan példát említeni, ahol a bytearray nem elegendő, szükséges a memoryview.

Értékadás

Általában egy utasításban egy változónak adunk értéket:

a = 3
b = a + 2

Egyszerre több változónak is értéket tudunk adni, pl:

a = b = c = 5

Itt mindhárom változó értéke 5 lesz. Ill.:

a, b, c = 3, 4, 5

Hatókör

A változóknak vannak hatóköreik (scope). Alapvetően kétféle hatókör létezik: globális és lokális. A lokális scope függvényre vonatkozik.

Alapértelmezésben látják a függvények a globális hatókörű változókat:

a = 2
 
def f():
    print(a) # 2
 
f()

Értékadásnál viszont létrejön egy lokális változó. Egy lokálisan létrehozott változó nem írja felül a globálist:

a = 2
 
def f():
    a = 3
    print(a) # 3
 
f()
print(a) # 2

Szerintem ez egy tervezési hiba. Ebben a formában én azt várnám, hogy a globális változó változzon. Ha függvényen belül az értékadásnál nem új változót szeretnénk létrehozni, hanem a globálisat módosítani, akkor először a global kulcsszóval meg kell adni, hogy az arra vonatkozik:

a = 2
 
def f():
    global a
    a = 3
    print(a) # 3
 
f()
print(a) # 3

A kettőt viszont nem vegyíthetjük; nem érhetjük el a globális változót és hozhatunk létre ugyanolyan nevű lokálisat egyszerre. (Szerintem egyébként ez is egy nyelv tervezési hiba.)

Változó törlése

A változók létrehozásával memóriát foglalunk le. Hosszú története van a memóriaterület felszabadításának. Primitíveket általában nem lehet törölni,d e abból többnyire kevés van, és kicsi. Nagyobb egységeket, pl. tömböket, objektumokat egyes nyelvekben (pl. C++) explicit fel kell szabadítani, míg másokban automatikusan törlődik, ha már nincs rá referencia (pl. Java). Viszont magát a változót általában nem lehet törölni. A Pythonban viszont erre is van lehetőség, mégpedig a del paranccsal:

print(a) # 3
del a
print(a) # NameError

Feltételkezelés

Az alábbi példákban feltételezzük, hogy van egy a és egy b szám változónk, pl.:

a = 3
b = 2

If

Feltételes végrehajtást a más programozási nyelvekben megszokott if utasítással tudunk megvalósítani. Azonban a pontos szintaxis sajnos több ponton is eltér a megszokottól.

Nem kell a zárójel, bár a nyelv nem tiltja. A végére pontosvesszőt kell írni:

if a > b:
    print("greater")

A Pythonban megszokott módon itt sincs blokk utasítás. Egyformára kell behúzni azokat az utasításokat, amelyeket az adott feltételen belül szeretnénk végrehajtani:

print("before")
if a > b:
    print("a is")
    print("greater")
    print("than be")
print("after")

A végrehajtandó blokk nem maradhat üresen. Ha csak a feltételt írtuk le, de egyelőre nem szeretnénk megvalósítani a blokkot (pl. az egyik fejlesztő feladata a feltételek leprogramozása, a blokkokat pedig egy másik fejlesztő valósítja meg), akkor a pass utasítást használhatjuk:

if a > b:
    pass

Else

Az else ág a más programozási nyelvekben megszokott módon, a már megismert Python szintaxissal valósítható meg:

if a > b:
    print("greater")
else:
    print("not greater")

Elif

Több feltétel esetén az elif kulcsszó alkalmazható. Az else if sajnos nem működik.

if a > b:
    print("greater")
elif a < b:
    print("less")
else:
    print("equal")

Egysoros if

Ha egyetlen műveletet szeretnénk végrehajtani, akkor az if utasítást írhatjuk egy sorba is:

if a > b: print("greater")

Egysoros if…else

Ugyanez viszont nem működik az if…else szerkezetben. (Ezt tehát hibás: if a > b: print("greater") else print("not greater").) A helyes szintaxis a más programozási nyelvekhez szokottak számára kissé szokatlan:

print("greater") if a > b else print("not greater")

A Pythonban nincs ún ternary operator (?:), de vegyük észre, hogy ez a struktúra helyettesíti azt. A megszokott ternary operator a következő:

cond ? a : b # ilyen nincs a Pythonban

Az egysoros if…else viszont más sorrendben ugyan, de lényegében ezt tartalmazza:

a if cond else b

Például:

a = 3
b = 2
c = a if a > b else b
print(c) # 3

Egysoros if…else if…else

Több ág esetén viszont itt nincs elif, hanem a következőt alkalmazhatjuk:

print("greater") if a > b else print("less") if a < b else print("equals")

Switch

Hát, switch utasítás, az sajnos nincs a Python-ban. Viszont ez olyan gyakori, hogy alternatív megoldások kialakultak.

Abban a speciális esetben, ha egy bemenő értéktől függő kimenő értéket szeretnénk, akkor használhatunk egy dict-et:

switcher = {
    1: "Monday",
    2: "Tuesday",
    3: "Wednesday",
    4: "Thursday",
    5: "Friday",
    6: "Saturday",
    7: "Sunday",
}
print(switcher.get(3))

Ha egy utasítást szeretnénk végrehajtani, és annak az eredményét szeretnénk visszakapni, akkor használhatunk ún. lambda kifejezést (ld. később):

switcher = {
    "add":
        lambda x, y: x + y,
    "subtract":
        lambda x, y: x - y,
    "multiply":
        lambda x, y: x * y,
}
 
print(switcher.get("add")(3, 2))

Általános esetben, ha több utasítást is végre szeretnénk hajtani (ráadásul nem is feltétlenül van visszatérési érték), akkor az egyes ágak utasításait külön függvényekben írhatjuk, és az alábbi módszert alkalmazhatjuk:

def add(a, b):
    result = a + b
    return result
 
def subtract(a, b):
    result = a - b
    return result
 
def multiply(a, b):
    result = a * b
    return result
 
switcher = {
    "add": add,
    "subtract": subtract,
    "multiply": multiply,
}
 
print(switcher.get("add")(3, 2))

Ciklus

for

Talán a legalapvetőbb ciklus a for ciklus, mégpedig az a változat, amikor egy változó egyesével sorban felveszi a megfelelő számértékeket. Nos, ilyen ciklus nincs a Pythonban, legalábbis közvetlenül nincs. A Pythonban csak az adatstruktúra elemein végiglépkedő (más nyelvekben: foreach) ciklus létezik, pl.:

fruits = ["apple", "orange", "banana"]
for fruit in fruits:
    print(fruit)

Viszont van itt egy "kiskapu": a for ciklus végiglépkedhet egy range elemein is, ami lényegében azt az eredményt adja, amiről a felvezető mondatban szó volt:

for i in range(5):
    print(i*i)

while

A fenti, négyzetszámokat kiíró algoritmus megvalósítása az elöl tesztelő while ciklussal:

i = 0
while i < 5:
    print(i*i)
    i += 1

break és continue

A break és a continue ugyanúgy működik, mint más programozási nyelvekben:

  • A break kilép az aktuális ciklusból.
  • A continue a ciklusmag elejére ugrik (a for esetében a következő elemet veszi).

A páratlan számok négyzeteinek a kiírása végtelen while ciklus, break és continue segítségével:

i = 0
while True:
    i += 1
    if (i >= 10):
        break
    if i % 2 == 0:
        continue
    print(i*i)

else a végén

A Pythonban a ciklusoknak van egy sajátos lehetőségük: else utasítás a végén:

fruits = ["apple", "orange", "banana"]
for fruit in fruits:
    print(fruit)
else:
    print("end of for cycle")

Ez esetben a gyümölcsnevek kiírása után kiírja ezt: end of for cycle. Hasonlóan működik a while esetében is:

i = 0
while i < 5:
    print(i*i)
    i += 1
else:
    print("end of while cycle")

Függvények

Függvény létrehozása

Az alábbi példában létrehozunk egy függvényt, ami kiírja, hogy Hello, world!, majd meghívjuk:

def say_hello():
    print("Hello, world!")
 
say_hello()

A Pythonban a függvényt def kulcsszóval vezetjük be, utána jön a függvény neve, majd zárójelben a paraméterek (a fenti példában nincs), utána kettőspontot kell tenni, és behúzva jön a függvény törzse. Valami mindenképp kell, hogy legyen; ha csak a fejlcet akarjuk megadni a törzs nélkül, akkor a pass utasítást használhatjuk:

def say_hello():
    pass

Paraméterek

A függvényeknek paramétert vagy vesszővel felsorolt paramétereket adhatunk:

def say_hello(name):
    print(f"Hello, {name}!")
 
say_hello("Csaba") # Hello, Csaba!

Fogalmak:

  • paraméter: a függvény szempontjából a bemenet neve, a fenti példában name
  • argumentum: a konkrét érték, a fenti példában Csaba

Visszatérési érték

Értéket visszaadni a return utasítással tudunk, pl.:

def add(a, b):
    return a * b
 
print(add(3, 2)) # 5

Ez tehát egy példa a több paraméterű függvényre. Alapértelmezésben abban a sorrendben kel megadnunk az argumentumokat, amilyen sorrendben a függévny várja a paramétereket.

Típusok megadása

Van szintaxis a paraméter típusok és a visszatérés típusának megadására is, de a tapasztalatom az, hogy a Python ezt nem veszi figyelembe:

def add(a: int, b: int) -> int:
    return a * b
 
print(add(3, 2)) # 5
print(add("Hello, ", "world!")) # Sajnos működik: Hello, world!

Név szerinti paraméterátadás

Átadáskor megadhatjuk a paraméterek neveit is. Ez esetben a sorrend is tetszőleges lehet:

def subtract(a, b):
    return a - b
 
print(subtract(b=2, a=3))

Megfelelő paraméterneveket használva az olvashatóságot is javítja.

Alapértelmezett paraméter értékek

A paramétereknek alapértelmezett értéket adhatunk a következőképpen:

def say_hello(name = "world"):
    print(f"Hello, {name}!")
 
say_hello("Csaba") # Hello, Csaba!
say_hello()        # Hello, world!

*args és **kwargs

A * operátorral jelezhetjük, hogy tetszőleges számú paraméter érkezhet. Ebből csak egy lehet. Általános konvenció szerint ennek a neve *args:

def print_args(*args):
    for arg in args:
        print(arg)
 
print_args("apple", "orange", "banana")

A operátorral megadott paraméter azt jelenti, hogy tetszőleges számú kulcs-érték párokat adhatunk meg. Ebből is csak egy lehet. Ennek a konvencionális neve kwargs, ami key-value arguments-re utal:

def print_dict(**kwargs):
    for key in kwargs:
        print(key, ":", kwargs.get(key))
 
print_dict(apple="alma", orange="narancs", banana="banán")

Tetszőlegesen kombinálhatóak a különböző paraméterek, de a sorrend adott:

  • Alapértelmezett érték nélküliek.
  • Alapértelmezett értékkel rendelkezők.
  • *args (vagy bármilyen más névvel).
  • **kwargs (vagy bármilyen más névvel).
  • A "normál" paraméterekkel kezdünk

Belső függvények

Függvényen belül is létrehozhatunk függvényt, az alábbi módon:

def outer(name):
    def inner(salute):
        return f"{salute}, {name}!"
    print(inner("Hello"))
 
outer("Csaba")

A belső függvény látja a külső függvény paraméterét.

Funkcionális programozás

A Python funkcionális nyelv olyan értelemben, hogy egy változó értékül kaphat függvényt, függvény paraméterül kaphat egy másik függvényt, és egy függvénynek lehet visszatérési típusa egy függvény. Az alábbi példa mindezeket bemutatja:

def add(a, b):
    return a + b
 
def multiply(a, b):
    return a * b
 
def get_operation(sign):
    if sign == "+":
        return add
    if sign == "*":
        return multiply
    return None
 
def perform_operation(operation, a, b):
    return operation(a, b)
 
operation_add = get_operation("+")
operation_multiply = get_operation("*")
 
print(perform_operation(operation_add, 3, 2)) # 5
print(perform_operation(operation_multiply, 3, 2)) # 6

Lambda

A lambda kifejezés egy szintaktikai cukorka azokra az esetekre, ha a függvény egyszerű (tipikusan egysoros), csak a bemenő paraméterektől függ, nincs mellékhatása és nem szeretnénk külön létre hozni, nevet adni neki. A lambda kulcsszóval használhatjuk. Az alábbi példa illusztrálja ezt a lehetőséget.

def perform_operation(operation, a, b):
    return operation(a, b)
 
print(perform_operation(lambda a, b: a + b, 3, 2)) # 5
print(perform_operation(lambda a, b: a * b, 3, 2)) # 6

Objektumorientáltság

A Python objektumorientált nyelv.

Osztály

Osztályt létrehozni a class kulcsszóval tudunk. Az osztály neve bármilyen legális azonosító lehet. Szokásos konvenció szerint az osztályneveket nagybetűvel kezdjük, utána kisbetűkkel írjuk, majd a további szókezdő karaktereket szintén nagybetűvel írjuk (CamelCase). A Java-tól eltérően nem kell, hogy a fájlnév egyezzen az osztálynévvel. A függvényeknél megszokott kettőspontot (:) ott is ki kell tenni, és az ott megszokott módon azonos mértékben kell behúzni az osztály tartalmát. Példányosításhoz nem kell a new kulcsszó, mint más objektumorientált nyelvekben, elég csak megadni az osztály nevét:

class MyClass():
    a = 3
    b = 2
 
myObject = MyClass()
print(myObject.a) # 3
print(myObject.b) # 2

A fenti példában létrehoztunk egy osztály MyClass néven, amelynek két attribútuma van: a és b. Készítettünk belőle egy példányt (myObject), majd kiírtunk a két adatmezőt.

Készítsünk el egy fokkal bonyolultabb forgatókönyvet!

myObject1 = MyClass()
myObject2 = MyClass()
print(myObject1.a) # 3
print(myObject1.b) # 2
myObject1.b = 4
print(myObject1.b) # 4
print(myObject2.b) # 2

Ebben a példában két példányt hoztunk létre. Példát láthatunk az attribútum megváltoztatására, és arra is, hogy az egyik objektumon végrehajtott attribútum változtatás nem módosítja a másik objektum ugyanolyan nevű mezőjét.

Konstruktor

Az init() függvény a konstruktor. Ennek első paramétere az adott objektum példányra mutat. Neve bármi lehet, a konvenció szerint self. Utána tetszőleges számú paraméter következhet. A példányosításkor ezeket a paramétereket kell megadni.

class MyClass():
    def __init__(self, a, b):
        self.a = a
        self.b = b
 
myObject = MyClass(3, 2)
print(myObject.a) # 3
print(myObject.b) # 2

A bevezető példában megadott módszer (ahol konstruktoron kívül adunk fix értéket) és a konstruktor vegyíthető, ahogy a konstruktor paramétereinek is lehetnek alapértelmezett értékeik.

Metódusok

A konstruktorhoz hasonlóan tetszőleges számú metódust adhatunk az osztályhoz. Ezeknek is az első paraméterünk az adott objektumra mutat, melyet híváskor nem kell explicit átadni, és konvenció szerint a neve self. Ezen keresztül érik el az objektum attribútumait.

class MyClass():
    def __init__(self, a, b):
        self.a = a
        self.b = b
 
    def add(self):
        return self.a + self.b
 
    def multiply(self):
        return self.a * self.b
 
myObject = MyClass(3, 2)
print(myObject.add()) # 5
print(myObject.multiply()) # 6

Túlterhelés

A klasszikus értelembe vett túlterhelés nincs a Pythonban. (Emlékeztetőül: a túlterhelés azt jelenti, hogy ugyanolyan névvel több metódust hozunk létre, és a paraméter lista különböző.) Viszont azzal, hogy lehet a paramétereknek alapértelmezett értékeket adni, tkp. részben megoldható a túlterhelés megvalósítása:

class MyClass():
    def __init__(self, a, b):
        self.a = a
        self.b = b
 
    def add(self, c=0):
        return self.a + self.b + c
 
myObject = MyClass(3, 2)
print(myObject.add()) # 5
print(myObject.add(4)) # 9

Ez egyben példa arra is, hogy a metódusoknak a self paraméteren kívül lehetnek egyéb paraméterei is.

Láthatóság

A Pythonban minden publikus. Ha valamit privátként szeretnénk kezelni, akkor konvenció szerint alul vonással (_) adjuk meg. Ezzel viszont csak jelezzük, hogy ezt privátként szeretnénk kezelni, a rendszer nem akadályozza meg azt, hogy hozzáférjünk.

Öröklődés

Örökölni úgy tudunk, hogy az osztálynév után megadjuk az ősosztály nevét. A többi objektumorientált programozási nyelvben megszokott módon a leszármazott osztály tartalmazza az ősosztály összes attribútumát és metódusát. Újakat is létrehozhat.

Az alábbi példában a leszármazott megvalósítja a kivonást:

class MyExtendedClass(MyClass):
    def subtract(self):
        return self.a - self.b
 
myExtendedObject = MyExtendedClass(3, 2)
print(myExtendedObject.add())      # 5
print(myExtendedObject.multiply()) # 6
print(myExtendedObject.subtract()) # 1

Felüldefiniálás

Ha a leszármazottban egy olyan metódust hozunk létre, melynek a teljes szignatúrája megegyezik az ősével, akkor azt felüldefiniáljuk (override). Az ősre kétféleképpen hivatkozhatunk:

  • A super() hívással: ekkor az ős függvényét kell megadni az esetleges paraméterekkel.
  • Az ős nevével: ez esetben át kell adni a self paramétert.

Az alábbi kódban mindkettőre láthatunk példát:

class MyExtendedClass(MyClass):
    def __init__(self, a, b, c):
        super().__init__(a, b)
        self.c = c
 
    def add(self):
        return MyClass.add(self) + self.c
 
    def multiply(self):
        return super().multiply() * self.c
 
myExtendedObject = MyExtendedClass(4, 3, 2)
print(myExtendedObject.add())      # 9
print(myExtendedObject.multiply()) # 24

Attribútumok hozzáadása és törlése

Attribútumokat utólag is adhatunk osztályhoz, és törölhetjük is:

class MyClass():
    a = 3
    b = 2
 
MyClass.c = 4
del MyClass.a
 
myObject = MyClass()
# print(myObject.a) # AttributeError
print(myObject.b)
print(myObject.c)

Statikus attribútumok

Klasszikus értelemben vett statikus attribútum nincs a Pythonban. Viszont itt is van egy trükk: ha létrehozunk "hagyományos" attribútumot (tehát nem a konstruktorban self-en keresztül), akkor az elérhető az osztálynéven keresztül is, és ez statikus, ill. az egyes példányokon keresztül is, akkor nem statikus. Pl.:

class MyClass():
    a = 3
 
myObject1 = MyClass()
myObject2 = MyClass()
 
print(myObject1.a) # 3
print(myObject2.a) # 3
print(MyClass.a)   # 3
 
myObject1.a = 4
myObject2.a = 5
MyClass.a = 6
 
print(myObject1.a) # 4
print(myObject2.a) # 5
print(MyClass.a)   # 6

Stringgé alakítás

Ha egy objektumot print() hívással kiírunk, akkor alapértelmezésben azt a memóriacímet írja ki, ahol az objektum található. Ez nem túl informatív; ha az objektum tartalmát szeretnénk ehelyett megjeleníteni, akkor az __str()__ függvényt kell megvalósítanunk:

class MyClass():
    a = 3
    b = 2
 
    def __str__(self):
        return f'({self.a}, {self.b})'
 
myObject = MyClass()
print(myObject) # (3, 2)

Valójában hajtódik végre az str(myObject) hívásra, ami implicit bekövetkezik a print() híváskor.

Függvénnyé alakítás

Ha megvalósítjuk a __call()__ metódust és példányosítunk, akkor azt függvényként használhatjuk:

class MyAdd:
    def __call__(self, a: int, b: int) -> int:
        return a + b
 
myAdd = MyAdd()
print(myAdd(3, 2)) # 5

Operátorok

Az operátorokat is felül tudjuk definiálni a megfelelő függvény megvalósításával. Az alábbi példában olyan osztály hozunk létre, melynek objektumai között értelmezett az összeadás (+) operátor:

class MyNum:
    def __init__(self, a):
        self.a = a
 
    def __add__(self, other):
        return self.a + other.a
 
myNum1 = MyNum(3)
myNum2 = MyNum(2)
print(myNum1 + myNum2)

Ily módon számos egyéb operátort felül tudunk definiálni:

  • Műveletek számokkal:
    • Összeadás: __add__(other), eredménye o1 + o2
    • Kivonás: __sub__(other), eredménye o1 - o2
    • Szorzás: __mul__(other), eredménye o1 * o2
    • Valós osztás: __truediv__(other), eredménye o1 / o2
    • Maradékos osztás eredménye: __floordiv__(other), eredménye o1 // o2
    • Maradékos osztás maradéka: __mod__(other), eredménye o1 % o2
    • Hatványozás: __pow__(other), eredménye o1 ** o2
  • Bitműveletek:
    • Bitmozgatás balra: __lshift__(other), eredménye o1 « o2
    • Bitmozgatás jobbra: __rshift__(other), eredménye o1 » o2
    • Bitenkénti logikai ÉS: __and__(other), eredménye o1 & o2
    • Bitenkénti logikai VAGY: __or__(other), eredménye o1 | o2
    • Bitenkénti logikai kizáró VAGY: __xor__(other), eredménye o1 ^ o2
    • Bitenkénti logikai tagadás: __invert__(), eredménye ~o1 (ez tehát egy unáris operátor, itt nincs másik)
  • Összehasonlítások:
    • Kisebb mint: __lt__(other), eredménye o1 < o2
    • Kisebb vagy egyenlő: __le__(other), eredménye o1 <= o2
    • Nagyobb mint: __gt__(other), eredménye o1 > o2
    • Nagyobb vagy egyenlő: __ge__(other), eredménye o1 => o2
    • Egyenlő: __eq__(other), eredménye o1 == o2
    • Nem egyenlő: __ne__(other)>@, eredménye i1 != o2

Kivételkezelés

A kivételkezelés során elkülönítjük a valós eredményt a hibáktól. Ez a módszer Pythonban is létezik.

Hiba

Példaként írjuk be a következőt:

print(5/0)

Eredményül nem számot kapunk, hanem a következő hibaüzenetet:

ZeroDivisionError: division by zero

Ha mi magunk szeretnénk megvalósítani egy osztó függvényt, akkor különbséget kellene tudnunk tenni aközött, hogy az osztásnak lesz egy eredménye, és hogy egy hiba miatt nem tudtuk azt kiszámolni.

try … except

A Pythonban a kivételkezelés fő struktúrája a try … except …:

try:
    print(5 / 0)
except:
    print('Some error occurred')

Bár ez így túl általános; adjunk nevet is a kivételnek, és írjuk ki az üzenetet:

try:
    print(5 / 0)
except Exception as exception:
    print(str(exception))  # division by zero

Specifikusabb except

Specifikusabb hibatípust és egyszerre több kivétel ágat is megadhatunk:

try:
    print(5 / 0)
except ZeroDivisionError as zero_division_error:
    print(str(zero_division_error))  # division by zero
except Exception as exception:
    print(str(exception))

A standard hibákról ezen az oldalon olvashatunk részletesebben: https://docs.python.org/3/library/exceptions.html.

else ág

A ciklusokhoz hasonlóan itt is lehet else ág, ami akkor fut le, ha nem történt kivétel:

try:
    print(5 / 2)
except ZeroDivisionError as zero_division_error:
    print(str(zero_division_error))
else:
    print('No error occurred')

finally

A finally ág mindenképp lefut.

try:
    print(5 / 2)
except ZeroDivisionError as zero_division_error:
    print(str(zero_division_error))
else:
    print('No error occurred')
finally:
    print('End of exception block.')

Saját kivétel

Mi magunk is létrehozhatunk kivételt úgy, hogy az Exception osztályból kell azt származtatni. Kivételt explicit módon a raise utasítással tudunk kiváltani. Az alábbi példa egy teljesen általános kivételkezelésim módszert tartalmaz több except ággal, else ággal és finally ággal is:

def f(a):
    if a < 0:
        raise NegativeError()
    if a > 5:
        raise TooLargeError()
    print(a + 1)
 
for i in [-2, 3, 7, 'apple']:
    try:
        f(i)
    except NegativeError:
        print("The passed value was negative")
    except TooLargeError:
        print("The passed value is too large")
    except Exception as e:
        print("Other error:" + str(e))
    else:
        print("No error occurred")
    finally:
        print("End of call")

Modulok

Modul létrehozása

Hozzunk létre egy fájlt mymodule.py néven, a következő tartalommal:

def say_hello(name):
    print(f'Hello, {name}!')
 
def add(a, b):
    return a + b

Ennyi.

Importálási módok

Egyik importálási módszer:

import mymodule
 
mymodule.say_hello('Csaba')
print(mymodule.add(3, 2))

Ha át szeretnénk nevezni:

import mymodule as mm
 
mm.say_hello('Csaba')
print(mm.add(3, 2))

Kisebb projektek esetén, ha csak kevés modult hazsnálunk, akkor teljesen meg is szabadulhatunk a prefixtől:

from mymodule import *
 
say_hello('Csaba')
print(add(3, 2))

Ha nem szeretnénk mindent importálni, csak amit használunk:

from mymodule import say_hello
 
say_hello('Csaba')

Ez egy jó gyakorlat olyan értelemben, hogy jobban kontroll alatt van, hogy mit használunk.

Modul tartalmának a kilistázása

A dir() függvénnyel ki tudjuk listázni azt, hogy mi található az adott modulban, pl.:

import mymodule
 
print(dir(mymodule)) # ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add', 'say_hello']

Modul egy másik könyvtárban

Ha az importálandó modul nem ugyanabban a könyvtárban van, mint az, amelyik használja (helyezzük át a mymodule.py fájlt a mypackage könyvtárba), akkor a következőképpen tudjuk importálni:

from mypackage.mymodule import say_hello
 
say_hello('Csaba')

Standard modulok

A Pythonnal számos beépített modulja van, melyekről még lesz szó bővebben. Ilyen például a matematika. A legnagyobb közös osztó meghívására példa:

import math
 
print(math.gcd(10, 15)) # 5
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License