Recimo, da delamo nekaj s kraji, opisanimi z imenom, zemljepisnimi koordinatami in številom prebivalcev. Če je teh krajev več, jih nočemo spravljati v posamične spremenljivke, temveč bo vsak kraj opisan s terko.
= ("Kremenik", 46.091158, 14.201702, 19)
kraj1 = ("Vogrsko", 45.905166, 13.708147, 908) kraj2
To je neprikladno: če želimo izračunati razdaljo med tema krajema (kot vemo, je Zemlja ploščata, zato smemo v ta namen uporabiti Pitagorov izrek), moramo pisati
from math import sqrt
1] - kraj2[1]) ** 2 + (kraj1[2] - kraj2[2]) ** 2) sqrt((kraj1[
0.527436784922135
Do imena kraja pridemo s kraj1[0]
, do števila
prebivalcev s kraj1[3]
.
0] kraj1[
'Kremenik'
3] kraj1[
19
Neugledno, nerodno, nepraktično, nevarno, ... skratka ne. Veliko raje
bi pisali - kot v vseh normalnih jezikih - kraj1.ime
,
kraj1.sirina
, kraj1.dolzina
in
kraj1.prebivalcev
.
Način, na katerega to storimo, se je v Pythonu sčasoma izboljševal. Pokazal bom aktualnega, čeprav bo vključeval uporabo razredov, ki se jih bomo učili šele v zadnjih tednih predavanj.
from typing import NamedTuple
class Kraj(NamedTuple):
str
ime: float
sirina: float
dolzina: int prebivalcev:
Po novem shranimo podatke o krajih tako:
= Kraj("Kremenik", 46.091158, 14.201702, 19)
kraj1 = Kraj("Vogrsko", 45.905166, 13.708147, 908) kraj2
Zdaj imamo, kot smo želeli,
kraj1.ime
'Kremenik'
kraj1.prebivalcev
19
- kraj2.sirina) ** 2 + (kraj1.dolzina - kraj2.dolzina) ** 2) sqrt((kraj1.sirina
0.527436784922135
Lepši je tudi izpis
kraj1
Kraj(ime='Kremenik', sirina=46.091158, dolzina=14.201702, prebivalcev=19)
Če nas pomuči nostalgija, pa sta to še vedno tudi terki:
0] kraj1[
'Kremenik'
3] kraj1[
19
= kraj1 ime, sir, vis, preb
ime
'Kremenik'
Poleg imen atributov smo našteli tudi njihove tipe. Snovalci Pythona vestno poudarjajo - in to obljubo bodo težko požrli - da bo označevanje tipov v Pythonu vedno namenjeno le knjižnicam in okoljem, ki nam pomagajo loviti napake, Python sam pa ujemanja tipov nikoli ne bo preverjal.
= Kraj(3.14, True, None, "malo") kraj
kraj
Kraj(ime=3.14, sirina=True, dolzina=None, prebivalcev='malo')
Pythonu je vseeno. Naš problem.
Vseeno je lepo, da se tudi mi držimo obljub; če obljubimo, da bo
nekaj int
, naj bo int
.
Kaj pa, če je lahko int
ali pa je neznano, kar bi radi
označili z None
?
from typing import NamedTuple, Optional
class Kraj(NamedTuple):
str
ime: float
sirina: float
dolzina: int] prebivalcev: Optional[
= Kraj("Stara Gora", 46.656897, 15.637519, None)
kraj
kraj
Kraj(ime='Stara Gora', sirina=46.656897, dolzina=15.637519, prebivalcev=None)
Kako povemo, da bo neka vrednost seznam nizov? Ali slovar, katerega ključi bodo cela števila, vrednosti pa, huh, seznami nizov? Označevanje tipov je lahko predmet čisto ločene "lekcije". Na hitro pa le povejmo, kako je s tem. Kot prvo, povemo lahko, da bo nekaj seznam, ne da bi se izjaznili glede tipa elementov.
list imena:
Če hočemo povedati, da bo vseboval nize, bomo rekli
list[str] imena:
Slovar, katerega ključi so int
, vrednosti pa
str
, bi bil
dict[int, str] po_starosti:
Če so vrednosti seznami nizov, pa
dict[int, list[str]] po_starosti:
Samo toliko, na hitro.
Atributi imajo lahko tudi privzete vrednosti.
from typing import NamedTuple, Optional
class Kraj(NamedTuple):
str
ime: float
sirina: float
dolzina: int] = None prebivalcev: Optional[
= Kraj("Stara Gora", 46.656897, 15.637519)
kraj
kraj
Kraj(ime='Stara Gora', sirina=46.656897, dolzina=15.637519, prebivalcev=None)
from typing import NamedTuple, Optional
class Kraj(NamedTuple):
str
ime: float = 46.119552
sirina: float = 14.838005
dolzina: int] = None prebivalcev: Optional[
= Kraj("Sreda") kraj
kraj
Kraj(ime='Sreda', sirina=46.119552, dolzina=14.838005, prebivalcev=None)
Če imamo nek kraj
kraj
Kraj(ime='Sreda', sirina=46.119552, dolzina=14.838005, prebivalcev=None)
in želimo spremeniti katero od njegovih lastnosti, nas bo to spomnilo, da imamo opravka s terkami.
= 368 kraj.prebivalcev
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[30], line 1
----> 1 kraj.prebivalcev = 368
AttributeError: can't set attribute
Terke so nespremenljive. Pač pa
=367) kraj._replace(prebivalcev
Kraj(ime='Sreda', sirina=46.119552, dolzina=14.838005, prebivalcev=367)
="Vače", prebivalcev=368) kraj._replace(ime
Kraj(ime='Vače', sirina=46.119552, dolzina=14.838005, prebivalcev=368)
Terka je še vedno nespremenljiva in zato nespremenjena: kraj je še vedno Sreda.
kraj
Kraj(ime='Sreda', sirina=46.119552, dolzina=14.838005, prebivalcev=None)
Metoda _replace
ima nekoliko zavajajoče ime, saj ničesar
ne spremeni, temveč sestavi novo terko. Če bi torej v resnici hoteli
"spremeniti" terko kraj
, bi morali -- podobno kot pri
nizih, kjer smo pisali ime = ime.replace("n", "r")
--
napisati
= kraj._replace(ime="Vače", prebivalcev=368) kraj
kraj
Kraj(ime='Vače', sirina=46.119552, dolzina=14.838005, prebivalcev=368)
NamedTuple in namedtuple.