Kde se asi stala chyba

Kde se asi stala chyba — typická otázka při hledání chyb v programovém kódu. Přestože dnešní programátor je vybaven celou paletou nástrojů od editoru po debugger, stále jsou zde případy chyb, které se nedaří tak jednoduše rozlousknout. Tyto chyby jsou až magické a zdá se, že čím více se na ně člověk zaměřuje, tím snáze unikají. V článcích Kde se asi stala chyba se pokusím ukázat pár takových chyb, které jsem hledal (a našel). Protože mým hlavním programovacím jazykem je Python, budou se ony chyby dotýkat právě Pythonu. Přestože na první pohled se chyby mohou zdát být jasné a zřejmé, jejich nalezení v komplikovaném kódu může být velice zdlouhavé.

První díl začneme zlehka chybou, kterou jsem řešil minulý týden. Začněme nezbytnou přípravou, a představme si funkci:

def get_vectors():
    vectors = [
        (1, 298, 35),
        (392, 401, 54),
        (325, 4, 8),
        (501, 648, 923)
    ]
    return vectors

Potud vše v pořádku, funkci máme, používáme ji ve svém kódu, funkce vrací vektory a všichni jsme spokojeni.

Nyní ale chceme přidat do funkce další vektor (9, 8, 7) na poslední místo v seznamu vektorů. Upravíme tedy funkci:

def get_vectors():
    vectors = [
        (1, 298, 35),
        (392, 401, 54),
        (325, 4, 8),
        (501, 648, 923)
        (9, 8, 7)
    ]
    return vectors

Po spuštění se však nestačíme divit:

Traceback (most recent call last):
  File "test.py", line 11, in <module>
    get_vectors()
  File "test.py", line 7, in get_vectors
    (9, 8, 7)
TypeError: 'tuple' object is not callable

Chyba samotná

Jak vidíte, samotný traceback nám toho příliš neřekne. Stejně tak pohled na řádku 7 zdrojového kódu. Problém totiž leží o řádek výše. A nyní — kdo z vás si všiml chybějící čárky na konci tohoto řádku?

Chybějící čárka způsobí to, že n-tice (9, 8, 7) je interpretována nikoli jako n-tice, ale jako parametry ve volání funkce, přičemž za funkci je považována n-tice na předchozím řádku. A n-tici v Pythonu jako funkci nezavoláme.

V tomto ukázkovém příkladu není až tak těžké chybu najít. Pokud si však představíme, že namísto prvků vektorů máme volání funkcí, popř. jiné složitější struktury, pak velice jednoduše začneme chybu hledat jinde.

Podobné případy

Podobnou legraci zažijeme i se seznamy, pokud kód pouze trochu upravíme:

def get_vectors():
    vectors = [
        ['jedna', 'dva']
        ['tri']
    ]
    return vectors

Při zavolání funkce get_vectors() opět dostaneme výjimku:

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    get_vectors()
  File "test.py", line 4, in get_vectors
    ['tri']
TypeError: list indices must be integers, not str

Příčina je obdobná — namísto záměny literálu n-tice za volání funkce (kulaté závorky) došlo k záměně literálu seznamu za indexaci (hranaté závorky).

A chybu můžeme dále lehce zobecnit na následující funkci (všimněte si opět chybějící čárky):

def get_vectors():
    vectors = [
        [3, 2]
        [1]
    ]
    return vectors

Ná kódu výše je nejzáludnější to, že je syntakticky správně, sémanticky správně a navíc za běhu nedojde k žádné výjimce. Přesto výsledek není ani v nejmenším to, co jsme očekávali, ale pouze seznam [2]!

Co z toho…

Ponaučení může být jednoduché — pokud chceme do kódu v Pythonu zapsat víceřádkový literál nějaké n-tice nebo seznamu, vždy pišme čárku i za poslední prvek, velice jednoduše pak můžeme kontejner doplnit o další prvek nebo prvky.

Kde se však toto pravidlo bude zaručeně plést je při psaní JSON: pokud v JSONu zapomenete poslední čárku, nejedná se o validní dokument a parser vám jej odmítne načíst.

Napsat komentář