Kódolási stílus – javaslatok

Czirkos Zoltán · 2020.08.18.

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:
    i = 1
    while i <= 100:
    	print(i)
    	i += 1
    A működő, de nem annyira szerencsés megoldások pedig:
    i = 1
    while i < 101:	# hol a „101” a feladat szövegében?
    	print(i)
    	i += 1
    i = 0
    while i < 100:	# hol a „+1” a feladat szövegében?
    	print(i + 1)
    	i += 1
    Vagy pl. „8 osztója van”:
    if osztok_szama(x) == 8:
    Nem annyira szerencsés:
    if osztok_szama(x) - 8 == 0: # miért?!
    Ezek ZH-kból származó példák voltak.
  • 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 egy class 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:
    if x < 0:
        pass   # nem kellene ezt megfordítani?
    else:
        print("nem negatív")
    Inkább írjuk ezt:
    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:
    for i in range(0, n + 1):
        if i == 0 or i == n:
            print("+", end="")
        else:
            print("-", end="")
    Érthetőbben ugyanaz:
    print("+", end="")
    for i in range(1, n):
        print("-", end="")
    print("+");
    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.
  • 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ű:
    def paros_e(szam):
        return szam % 2 == 0
    Nem nyújt többet, viszont sokkal hosszabb:
    def paros_e(szam):
        if szam % 2 == 0:  # „ha igaz, akkor igaz, ha hamis, akkor hamis”
            return True
        else:
            return False
    Ugyanez a helyzet a hívás helyén. Helyes, rövid, egyszerű:
    if paros_e(x):
    	...
    Nem nyújt többet, viszont sokkal hosszabb:
    if paros_e(x) == True:
    	...
  • Figyeljünk arra, hogy a függvényeinknek minden ágon ugyanaz legyen a visszatérési típusa! Helytelen:
    def van_osztoja(szam):
        for oszto in range(2, szam//2 + 1):
            if szam % oszto == 0:
                return True
    Ez a függvény None-t ad vissza, ha a számnak nincs osztója. Helyesen:
    def van_osztoja(szam):
        for oszto in range(2, szam//2 + 1):
            if szam % oszto == 0:
                return True
        return False
    Így már mindig bool a visszatérési érték.