Kódolási stílus – javaslatok
Czirkos Zoltán · 2021.05.31.
Megjegyzések és javaslatok a szépen írt, áttekinthető programokhoz.
Érdemes szem előtt tartani a következő dolgokat.
Olvashatóság – alapok
- Írjunk jól olvasható kódot. Ennek a legegyszerűbb módja azt írni, amit gondolunk. Ne trükközzünk a nyelvi elemekkel a szép, egyszerű programok írása helyett!
- Ne optimalizáljunk feleslegesen. Csak akkor, ha kiderül, hogy
lassú a programunk. Különösen ne végezzünk olyan mikrooptimalizációkat kézzel,
pl.
%2
helyett&1
rossz ötlet. - Törekedjünk arra, hogy a programkódunk (a feladat megoldása) minél
jobban hasonlítson a feladat szövegére. Ne vigyünk bele a megoldásba
felesleges csavarokat! Pl. ha 1-től 100-ig ki
kell írni a számokat, a szép megoldás ez:
A működő, de nem annyira szerencsés megoldások pedig:i = 1 while i <= 100: print(i) i += 1
i = 1 while i < 101: # hol a „101” a feladat szövegében? print(i) i += 1
Vagy pl. „8 osztója van”:i = 0 while i < 100: # hol a „+1” a feladat szövegében? print(i + 1) i += 1
Nem annyira szerencsés:if osztok_szama(x) == 8:
Ezek ZH-kból származó példák voltak.if osztok_szama(x) - 8 == 0: # miért?!
- Használjunk néven nevezett konstansokat és felsorolt típusokat mágikus
számok helyett! Nem szerencsés:
if evszak == 2
. Jobb ennél:if evszak == NYAR
. - Használjunk beszédes változóneveket, és használjunk függvényeket a részfeladatokhoz!
Például: mit csinál a következő program?
d = 2 a = 0 while a < 20: b = 1 c = 2 while b and c <= d/2: if d % c == 0: b = 0 c += 1 if b: print(d) a += 1 d += 1
Megoldás
Ugyanazt, mint ez:
def prim_e(szam): for oszto in range(2, szam//2 + 1): if szam % oszto == 0: return False return True def main(): vizsgalt = 2 db = 0 while db < 20: if prim_e(vizsgalt): print(vizsgalt) db += 1 vizsgalt += 1 main()
Karbantarthatóság – a program struktúrája
- Soha, de soha ne copy-pasteljünk kódot! A copy-pastelt kód helyett használjunk ciklust, listát vagy függvényt a feladattól függően.
- Függvény – hacsak nem ez a feladata – nem csinál I/O-t.
Azért lett függvény, hogy paraméterekben kapjon és visszatérési
értékben adjon vissza dolgokat. Az I/O a hívó
és a felhasználói felület dolga. Az esetleges diagnosztikai kiíratások
persze lehetnek kivételek, de az is inkább egy naplózó keretrendszer
használatával (vagy pl.
if DEBUG
-okkal védve) javasolt. - Függvény megírásánál mindig, külön kérés nélkül feltételeznünk kell
azt, hogy többször is meg fogják hívni. Így a függvénynek nem lehet
felesleges mellékhatása. Például egy kártyapaklit keverő függvénynek
valószínűleg nem dolga
random.seed
-et hívni. - Szintén függvények: ne éljünk olyan előfeltételezésekkel, amelyekről a feladat nem ír, vagy amelyek a hívótól nem várhatóak el. Például ha a függvényünk dolga az, hogy megszámlálja egy szöveg magánhangzóit, és azok darabszámait betegye egy listába, akkor ennek dolga létrehozni a listát.
- Függvény ne hívjon kilépéssel, hasonlókkal kapcsolatos dolgokat, mert a
felsőbb szintű kódot ez meglepetéssel fogja érinteni (fájlok lezárása,
takarítás elmarad). Ha szükséges, akkor legyen a programnak egy
fatal_error()
függvénye, azt hívjuk. - Alakítsunk ki értelmes adatszerkezeteket. Fogjuk össze osztályba
az összetartozó adatokat. Például
szelesseg, magassag, elem
tipikusan egyclass Palya
nevű típusba való. Gondoljunk arra, hogy legjobb a feladatunkban megjelenő mindenféle entitáshoz egy külön típust rendelnünk a programban. - Legyen egyértelmű specifikáció arról, hogy mik a paraméterek és kinek a felelősége ellenőrizni a bemenő paraméterek értelmességét.
Nyelvi eszközök
- Ne írjunk a feltételekhez üres igaz ágat, inkább tagadjuk a feltételt! Tehát ehelyett:
Inkább írjuk ezt:if x < 0: pass # nem kellene ezt megfordítani? else: print("nem negatív")
if x >= 0: print("nem negatív")
- Ne használjunk
break
-et,continue
-t feleslegesen. Egy-két speciális esettől eltekintve szebb kódot lehet írni nélkülük. - Ne írjunk loop-switch szekvenciákat (loop-switch sequence, for-case paradigm). Így nevezzük azt,
amikor egymás után következő műveletek kerülnek feleslegesen ciklusba, hogy aztán azon belül esetszétválasztással
kelljen újra szétválasztani őket. Pl. a
+----+
karaktersorozat kiírásához, nem túl jó megoldás:
Érthetőbben ugyanaz:for i in range(0, n + 1): if i == 0 or i == n: print("+", end="") else: print("-", end="")
Az előbbi nem fejezi ki az egymásutániságot, az utóbbin viszont ránézésre látszik, hogy előbb plusz van, utána mínuszok, végül megint egy plusz.print("+", end="") for i in range(1, n): print("-", end="") print("+");
- Hiába van rá mód, lehetőleg ne keverjük a logikai és az aritmetikai kifejezéseket!
Például ha számokat vizsgálunk, igenis írjuk ki az
== 0
-t vagy a!= 0
-t a feltételekben. - Használjuk a
bool
típust, ahol logikai értékekkel dolgozunk! - Ne bonyolítsuk a logikai függvényeket redundáns
if ... return True else return False
vezérlési szerkezettel. Helyes, rövid, egyszerű:
Nem nyújt többet, viszont sokkal hosszabb:def paros_e(szam): return szam % 2 == 0
Ugyanez a helyzet a hívás helyén. Helyes, rövid, egyszerű:def paros_e(szam): if szam % 2 == 0: # „ha igaz, akkor igaz, ha hamis, akkor hamis” return True else: return False
Nem nyújt többet, viszont sokkal hosszabb:if paros_e(x): ...
if paros_e(x) == True: ...
- Figyeljünk arra, hogy a függvényeinknek minden ágon ugyanaz legyen a visszatérési típusa! Helytelen:
Ez a függvénydef van_osztoja(szam): for oszto in range(2, szam//2 + 1): if szam % oszto == 0: return True
None
-t ad vissza, ha a számnak nincs osztója. Helyesen:
Így már mindigdef van_osztoja(szam): for oszto in range(2, szam//2 + 1): if szam % oszto == 0: return True return False
bool
a visszatérési érték.