Pri izpisovanju smo bili doslej omejeni na dokaj zoprni
print
. Za začetek nam je zadoščal, kmalu pa postane
neroden, saj nekaterih stvari z njim preprosto ni mogoče (preprosto)
narediti. Za začetek se je potrebno z njim pogajati, da ne gre po vsakem
izpisu v novo vrsto in da ne dodaja presledkov med reči, ki jih
izpisujemo.
Če smo imeli slovar, v katerem je pisalo, koliko kilometrov je nekdo prevozil s katerim izmed svojih koles,
= {'Canyon': 2932, 'Cube': 2939,
razdalje 'Nakamura': 488, 'Stevens': 0}
in hoteli to izpisati, nam je print
dodajal presledke
pred dvopičja.
for kolo, razdalja in razdalje.items():
print(kolo, ":", razdalja)
Canyon : 2932
Cube : 2939
Nakamura : 488
Stevens : 0
Če bi želeli poleg tega v oklepjih izpisati, kakšen delež je prevozil s katerim kolesom, bi dobili kole neprebavljivost:
= sum(razdalje.values())
skupna
for kolo, razdalja in razdalje.items():
print(kolo, ":", razdalja, "(",
/ skupna * 100, "%)") razdalja
Canyon : 2932 ( 46.10787859726372 %)
Cube : 2939 ( 46.217958798553234 %)
Nakamura : 488 ( 7.6741626041830475 %)
Stevens : 0 ( 0.0 %)
Preveč decimalk, tile grozni presledki ... morda pa bi se prileglo tudi kaj poravnavanja, ne?
Slaba novica: print
-u sicer lahko rečemo, naj ne piše
odvečnih presledkov, s poravnavanjem in decimalkami pa se mu res ne da
ukvarjati.
Večina besedil, ki jih izpišejo ali kako drugače pokažejo programi, vsebuje fiksno besedilo, v katerega so vstavljeni kaki nizi ali številke. Če računalnik izpiše "Datoteke kraljice.py ni mogoče najti", je vstavljeno besedilo "kraljice.py", ki je najbrž vsakič drugačno, odvisno pač od tega, katero datoteko (neuspešno) iščemo. V "Danes je 60 Fahrenheitov, to je, 16 Celzijevih stopinj" sta vstavljeni števili 60 in 16, ki sta odvisni od tega, v katerem letnem času poženemo program (in koliko smo že uspeli pogreti planet), ostalo besedilo pa je vedno enako.
Večina programskih jezikov - ali pa pripadajočih knjižnic - ima kak mehanizem, s katerim lahko sestavimo nize tako, da napišemo "konstantni" niz, označimo mesta, na katera je potrebno nekaj vstaviti. Potem pa na nek način povemo, kaj naj vstavi. Popularni prevajani jeziki so večinoma povzeli način, ki so si ga izmislili v jeziku C in je malo bolj zoprn, novejši jeziki, predvsem skriptni, ki so bližje spletu, pa navadno omogočajo preprostejše in zmogljivejše načine sestavljanja nizov iz vzorcev.
Eno od načel Pythona je, da se da vsako stvar narediti na en in samo en očiten način. To je dobro zato, ker, posledično, vsi programerji pišemo podobne programe in zato lažje sodelujemo. Prav pri oblikovanju nizov pa se je to sfižilo: nize lahko oblikujemo na tri različne načine. Do tega je prišlo, ker so določene ideje (ne le v Pythonu) zorele z leti, vendar (za)star(el)ih načinov ne moreš kar tako ukiniti, saj starejši programi potem ne bi več delali v novejših različicah jezika. Zaresen programer v Pythonu mora poznati vse tri načine, nam bo zadoščal zadnji, najmodernejši. V resnici tudi jaz, zaresen programer, uporabljam le še tega.
Recimo, da imamo
= 42
fahr = (fahr - 32) * 5 / 9 celz
Zdaj bi radi sestavili, v bistvu, takšen niz:
"{fahr} Fahrenheitov je {celz} Celzijev"
'{fahr} Fahrenheitov je {celz} Celzijev'
le da želimo, da Python na mesti, ki smo ju označili s
{fahr}
in {celz}
, vstavi vrednosti
spremenljivk fahr
in celz
.
To počnejo f-nizi. F-niz je kot običajen niz, le da mu pred prvi narekovaj dodamo črko f (kot format). V takšnih nizih imajo zaviti oklepaji poseben pomen: kar je v njih, se (izračuna in) vstavi.
f"{fahr} Fahrenheitov je {celz} Celzijev"
'42 Fahrenheitov je 5.555555555555555 Celzijev'
V neki stari domači nalogi smo - oh, kako je bilo to nerodno! - pisali
from random import randint
= randint(2, 10)
a = randint(2, 10)
b print(a, "krat", b)
= input("Odgovor? ") c
S tem, da najprej izpišemo in potem zahtevmo odgovor, smo se izognili
temu, da bi morali mukoma sestavljali niz ob klicu funkcije
input
:
= randint(2, 10)
a = randint(2, 10)
b = input("Koliko je " + str(a) + "x" + str(b) + "?") c
Zdaj, ko poznamo metodo f-nize, poznamo preprostejši način:
= randint(2, 10)
a = randint(2, 10)
b = input(f"Koliko je {a}x{b}? ") c
(Tule je priložnost, da mimogrede pokažemo starejša načina
oblikovanja nizov, ki smo ju omenjali na začetku: v C-jevskem slogu bi
pisali c = input("Koliko je %sx%s? " % (a, b))
, v onem,
malo novejšem pa
c = input("Koliko je {}x{}? ".format(a, b))
. Obema je
skupno to, da so v nizu le označena mesta, kamor je potrebno vstavljati,
potem pa moramo na nekem drugem mestu, po koncu niza, našteti stvari, ki
jih vstavljamo. Z novejšim slogom lahko z nekimi čudnimi triki v zavite
oklepaje dodajamo tudi imena spremenljivk, vendar niti približno tako
udobno kot po novem. Vsak od starih načinov ima tudi kakšno lastnost,
zaradi katere ga moramo včasih uporabiti tudi v novih programih v
Pythonu. Vendar se to zgodi zelo redko in v zelo posebnih primerih.)
Na ta način ne vstavljamo le števil, temveč karkoli, kar se da izpisati.
= "kraljice.py"
datoteka f"Datoteke {datoteka} ne najdem"
'Datoteke kraljice.py ne najdem'
V nize ne vstavljamo le vrednosti spremenljivk: v zavite oklepaje lahko pišemo kar cele izraze.
= 42
fahr
f"{fahr} Fahrenheitov je {(fahr - 32) * 5 / 9} Celzijev"
'42 Fahrenheitov je 5.555555555555555 Celzijev'
Zdaj pa spet na kolesa!
= sum(razdalje.values())
skupna
for kolo, razdalja in razdalje.items():
print(f"{kolo}: {razdalja} ({razdalja / skupna * 100} %)")
Canyon: 2932 (46.10787859726372 %)
Cube: 2939 (46.217958798553234 %)
Nakamura: 488 (7.6741626041830475 %)
Stevens: 0 (0.0 %)
Delen uspeh. Znebili smo se presledkov pred dvopiči, predvsem pa je postalo samo izpisovanje preglednejše. Kar primerjajmo:
prej: print(kolo, ":", razdalja, "(", razdalja / skupna * 100, "%)")
zdaj: print(f"{kolo}: {razdalja} ({razdalja / skupna * 100} %)")
Če hočemo še pregledneje, pa lahko napišemo
= sum(razdalje.values())
skupna
for kolo, razdalja in razdalje.items():
= razdalja / skupna * 100
delez print(f"{kolo}: {razdalja} ({delez} %)")
Canyon: 2932 (46.10787859726372 %)
Cube: 2939 (46.217958798553234 %)
Nakamura: 488 (7.6741626041830475 %)
Stevens: 0 (0.0 %)
Zdaj je f-niz očitno vzorec, v katerega vstavljamo vrednosti.
Ostane nam še boj z decimalkami.
Če bi radi povedali, kako naj se izpiše neko število (ali niz ali karkoli že), dodamo izrazu dvopičje in za njim opis formata.
To bomo najpogosteje potrebovali za oblikovanje necelih števil. To gre tako:
f"{fahr:4.1f} Fahrenheitov je {celz:4.1f} Celzijev"
'42.0 Fahrenheitov je 5.6 Celzijev'
Opis formata je tule 4.1f
. Pri tem 4.1
pomeni, da bi radi izpis na štiri mesta, pri čemer naj bo eno
rezervirano za decimalko. Za črko f
si predstavljajte, da
pomeni float
.
Če pustimo za število premalo prostora, ga bo izpis pač zasedel več.
= 1234.5678
x f"Primer predolgega števila: {x:3.1f}"
'Primer predolgega števila: 1234.6'
Zahtevali smo eno decimalko in to smo tudi dobili, vse skupaj pa je širše kot štiri mesta, saj je število pač predolgo.
Določanje števila decimalk je smiselno le pri necelih številih. Če vstavljamo nize ali cela števila, lahko določamo le širino:
= [
podatki 74, "Anze", False),
(82, "Benjamin", False),
(48, "Cilka", True),
(66, "Dani", False),
(61, "Eva", True),
(101, "Franc", False),
(
]
for teza, ime, spol in podatki:
print(f"{ime:10}{teza:6}")
Anze 74
Benjamin 82
Cilka 48
Dani 66
Eva 61
Franc 101
Tu za številom mest nismo dodajali črke f
, saj ne gre za
(necela) števila. Za nizi pa sploh ne. (Obstajajo sicer druge črke, ki
bi jih lahko dodali, da Python povemo, za kaj gre, vendar jih smemo tule
brez škode pozabiti.)
Vsa imena so izpisana z desetimi znaki; manjkajoči prostor je zapolnjen s presledki. Številke so izpisane s šestimi mesti, manjkajoči prostor spet zapolnjujejo presledki.
Nizi so poravnani na levo - presledki so dodani za njimi. Večina
reči, ki jih izpisujemo (nizi in števila namreč niso edino, kar se da
izpisati), so poravnani tako. Številke pa so poravnane desno. To lahko
spremenimo, če za dvopičje dodamo <
, >
ali ^
, s katerimi naročimo, naj bo reč poravnana levo,
desno ali na sredino. Ravno obratni izpis kot zgoraj bi dosegli z
for teza, ime, spol in podatki:
print(f"{ime:>10}{teza:<6}")
Anze74
Benjamin82
Cilka48
Dani66
Eva61
Franc101
To ni ravno posrečeno. Dodajmo še presledek.
for teza, ime, spol in podatki:
print(f"{ime:>10} {teza:<6}")
Anze 74
Benjamin 82
Cilka 48
Dani 66
Eva 61
Franc 101
To je lažje berljivo, še vedno pa je seveda najlepši prvi izpis.
Pred znak, ki določa poravnavo, lahko dodamo simbol, ki naj se uporabi za zapolnjevanje - če seveda nismo zadovoljni s presledkom. Poravnajmo imena levo, števile desno, namesto presledkov pa zapolnimo prazen prostor s pikami.
for teza, ime, spol in podatki:
print(f"{ime:.<10}{teza:.>6}")
Anze..........74
Benjamin......82
Cilka.........48
Dani..........66
Eva...........61
Franc........101
Namesto pik lahko uporabimo poljuben drug znak. Kadar takole
zapolnjujemo prostor s čimerkoli drugim kot s presledki, moramo dodati
znake za poravnavanje (<
, >
ali
^
), tudi kadar z njimi izberemo takšno poravnavanje, kot bi
bilo tudi privzeto (desno za števila, levo za vse drugo) všeč.
V vse detajle ne bomo šli. Določiti je mogoče še veliko reči. Pri številih lahko zahtevamo, naj se vedno izpiše predznak, torej +, kadar je število pozitivno. Za števila lahko določimo, naj se izpišejo dvojiško ali šestnajstiško... Kdor potrebuje več kot to, naj Googla.
Nazaj h kolesom. Večina izpisa je v redu, le deleže bi radi izpisali
z manj decimalkami - recimo z eno samo. Dovolj je dodati
:.1f
.
= sum(razdalje.values())
skupna
for kolo, razdalja in razdalje.items():
print(f"{kolo}: {razdalja} ({razdalja / skupna * 100:.1f} %)")
Canyon: 2932 (46.1 %)
Cube: 2939 (46.2 %)
Nakamura: 488 (7.7 %)
Stevens: 0 (0.0 %)
Celotna zgodba s kolesi je sicer nekoliko daljša. Podatke smo prebrali iz datoteke.
= {"Canyon": 0, "Cube": 0, "Nakamura": 0, "Stevens": 0}
voznje = {"Canyon": 0, "Cube": 0, "Nakamura": 0, "Stevens": 0}
razdalje = {"Canyon": 0, "Cube": 0, "Nakamura": 0, "Stevens": 0}
visine
for vrstica in open("kolesa.txt"):
= vrstica.split(",")
kolo, razdalja, visina += 1
voznje[kolo] += int(razdalja)
razdalje[kolo] += int(visina) visine[kolo]
V slovarjih voznje
, razdalje
in
visine
imamo število voženj ter skupno razdaljo in višino,
prevoženo s posameznim kolesom. Kaj, ko bi to izpisali v obliki
tabelice?
= sum(voznje.values())
sk_voz = sum(razdalje.values())
sk_raz = sum(visine.values())
sk_vis
for kolo in voznje:
= voznje[kolo], razdalje[kolo], visine[kolo]
voz, raz, vis print(f"{kolo:12}{voz:5} ({voz / sk_voz:>5.1%}) {raz:10} "
f"({raz / sk_raz:>5.1%}) {vis:10} ({vis / sk_vis:>5.1%}) ")
Canyon 26 (26.0%) 2766 (39.6%) 26392 (27.0%)
Cube 43 (43.0%) 3174 (45.4%) 66705 (68.3%)
Nakamura 22 (22.0%) 439 ( 6.3%) 1119 ( 1.1%)
Stevens 9 ( 9.0%) 607 ( 8.7%) 3395 ( 3.5%)
Novo je oblikovanje odstotkov: ne množimo s 100 in ne uporabimo
f
(recimo, zgoraj. .1f
) temveč %
.
Konkretno, napisali smo {voz / sk_voz:>5.1%}
. Količnik
voz / sk_voz
smo poravnali desno, izpisali na pet mest z
eno decimalko in sicer kot odstotek. Ta pomeni, da bo Python reč kar sam
pomnožil s 100
in še znak %
bo dodal.
Mimogrede opazíte še, da smo spremenljivkam voz
,
raz
in vis
dali kratka imena, skupnemu številu
voženj, dolžini in razdalji pa podobna, prav tako kratka imena. Že s
tako kratkimi je vrstica kar dolga.
Izpis je videti že kar imenitno; še bolj imeniten bo, če mu dodamo imena stolpcev.
= sum(voznje.values())
sk_voz = sum(razdalje.values())
sk_raz = sum(visine.values())
sk_vis
print()
print(f"{'Kolo':6}{'Število voženj':>19}{'Razdalja':>19}{'Višina':>19}")
print("-" * (6 + 3 * 19))
for kolo in voznje:
= voznje[kolo], razdalje[kolo], visine[kolo]
voz, raz, vis print(f"{kolo:12}{voz:5} ({voz / sk_voz:>5.1%}) {raz:10} "
f"({raz / sk_raz:>5.1%}) {vis:10} ({vis / sk_vis:>5.1%})")
print("-" * (6 + 3 * 19))
Kolo Število voženj Razdalja Višina
---------------------------------------------------------------
Canyon 26 (26.0%) 2766 (39.6%) 26392 (27.0%)
Cube 43 (43.0%) 3174 (45.4%) 66705 (68.3%)
Nakamura 22 (22.0%) 439 ( 6.3%) 1119 ( 1.1%)
Stevens 9 ( 9.0%) 607 ( 8.7%) 3395 ( 3.5%)
---------------------------------------------------------------
Tako lepo, da je že kar malo kičasto.