Vrácení jako dict
def load_aff_rules(...) -> dict:
rules = {'normal': [...], 'reversed': [...]}
return rules
- Výhody
- Čitelná pojmenovaná data:
rules['normal']
,rules['reversed']
. - Snadno se rozšiřuje o další klíče.
- Čitelná pojmenovaná data:
- Nevýhody
- Nutnost pamatovat si přesné názvy klíčů – hrozí
KeyError
. - Horší podpora statické kontroly typů (mypy, IDE).
- O něco pomalejší přístup kvůli hash‑lookup v dictu.
- Nutnost pamatovat si přesné názvy klíčů – hrozí
Vrácení jako Tuple[List[AffRule], List[AffRule]]
from typing import Tuple, List
def load_aff_rules(self, ...) -> Tuple[List[AffRule], List[AffRule]]:
normal, reversed = [...], [...]
return normal, reversed
a skutečně:
return rules['normal'], rules['reversed']
- Výhody
- Jasně daná struktura: vždy dva seznamy ve stanoveném pořadí.
- Snadné rozbalení do proměnných:
normal_rules, reversed_rules = self.load_aff_rules(...)
- Lepší podpora statické kontroly typů díky
Tuple[...]
. - Rychlejší přístup (není potřeba dict‑lookup).
- Nevýhody
- Méně samo‑dokumentující – musíte znát pořadí výstupů.
- Obtížnější rozšíření – přidání dalšího výstupu změní definici tuple i všech volání.
Použití čárek
V definici typu:
-> Tuple[List[AffRule], List[AffRule]]
čárka odděluje první a druhý seznam.
V návratové hodnotě:
return rules['normal'], rules['reversed']
čárka tu znamená, že Python vrátí 2‑prvkový tuple
.
Dodatek: Chyba AttributeError
při rozbalování dict
Chyba:
AttributeError: 'str' object has no attribute 'matches'
Tato chyba vzniká při nesprávném rozbalení návratové hodnoty z funkce, která vrací
dict
. Pokud máš následující kód:
aff_rules["normal"], aff_rules["reversed"] = self.load_aff_rules(...)
a funkce load_aff_rules
vrací dict
, dochází k tomu, že Python
se snaží rozbalit klíče slovníku, ne jeho hodnoty. Výsledkem je:
aff_rules["normal"] == "normal"
aff_rules["reversed"] == "reversed"
což následně vede k chybě:
AttributeError: 'str' object has no attribute 'matches'
Řešení
Varianta A – Nechat návratový typ jako dict
Vrácený dict
zachováš beze změny, ale rozbalíš ho ručně:
aff_rules = self.load_aff_rules(...)
normal_rules = aff_rules["normal"]
reversed_rules = aff_rules["reversed"]
for w in dic_entries["normal"]:
for r in normal_rules:
if r.matches(w):
dics_out["normal"].append(r.apply(w))
Varianta B – Vrátit Tuple
místo dict
Pokud změníš návratovou hodnotu funkce na:
def load_aff_rules(...) -> Tuple[List[AffRule], List[AffRule]]:
return rules["normal"], rules["reversed"]
pak volání funkce bude:
normal_rules, reversed_rules = self.load_aff_rules(...)
a zbytek kódu funguje stejně, ale s jistotou, že máš dva seznamy objektů typu AffRule
.
Výhodou této varianty je lepší podpora typové kontroly (mypy
, IDE),
rychlejší přístup (není potřeba hash-lookup jako u dict
)
a menší riziko chyb typu KeyError
.
* * *
def parse_aff_input(self, aff_path: str) -> list[dict]:
Vysvětlení deklarace výstupního typu
Funkce skutečně vrací seznam slovníků, kde každý slovník má přesně tyto klíče a hodnoty:
'regex'
:str
'substring'
:str
'replacement'
:str
'ext'
:str
'tags'
:List[str]
Původní podpis
def parse_aff_input(self, aff_path: str) -> list[dict]:
Toto je sice validní, ale statický typ dict
neříká nic o tom, jaké klíče či hodnoty slovníky obsahují.
Konkrétní typová anotace
Můžeme si nejprve vytvořit TypedDict, aby bylo jasné, co v dictu očekáváme:
from typing import TypedDict, List
class AffRecord(TypedDict):
regex: str
substring: str
replacement: str
ext: str
tags: List[str]
A pak upravit deklaraci:
def parse_aff_input(self, aff_path: str) -> List[AffRecord]: ...
Alternativa bez TypedDict
Pokud nechcete zavádět TypedDict
, můžete alespoň upřesnit hodnoty pomocí Union
:
from typing import List, Dict, Union
def parse_aff_input(self, aff_path: str) -> List[Dict[str, Union[str, List[str]]]]:
...
Tím dáváte najevo, že každá položka seznamu je slovník s řetězcovými klíči a hodnotami buď str
nebo List[str]
.
Žádné komentáře:
Okomentovat