Tulostuksen muotoilusta
Ennen kuin jatketaan toistokäskyistä ja niitä käyttävistä esimerkeistä, katsotaan vähän sitä, miten ohjelmoija voi vaikuttaa ohjelman tulostuksen muotoiluun.
Tähänastissa esimerkeissä desimaaliluvut on tulostettu sillä tarkkuudella, mitä Python-tulkki on oletuksena käyttänyt. Käytännössä tämä on tarkoittanut sitä, että tulostetuissa luvuissa on usein ollut tarpeettoman monta desimaalia. Tulostuksesta tulisi selvästi siistimpi, jos desimaalipisteen jälkeen tulevien numeroiden määrää rajoitettaisiin. Tämä voidaan tehdä tulostuksen muotoilun avulla. Täsmällisesti ottaen kysymys on oikeasti merkkijonojen muotoilusta, eli tulostuksen muotoiluun vaikutetaan muotoilemalla tulostettavaa merkkijonoa.
Muotoilun avulla voidaan myös määritellä, miten leveään kenttään kokonaisluvut ja merkkijonot tulostetaan. Kentällä tarkoitetaan sitä aluetta, joka tulostuksessa käytetään jonkin asian esittämiseen. Jos kentän leveydeksi on määritelty esimerkiksi 8, niin luvulle varataan tulostuksessa 8 merkin levyinen alue, vaikka itse luku veisikin vain 2 merkkiä. Joko ennen lukua tai sen jälkeen kirjoitetaan ylimääräisiä välilyöntejä. (Jos luku on niin suuri, että se ei mahdu sille varattuun kenttään, osaa luvusta ei kuitenkaan jätetä tulostamatta, vaan luvun annetaan käyttää enemmän tilaa kuin mitä sille on muotoilussa määrätty.) Kentän leveyden määrittelyä tarvitaan esimerkiksi silloin, kun halutaan tulostaa useita arvoja peräkkäisille riveille niin, että vastaavat arvot tulevat rivillä aina samaan kohtaan.
Muotoilun voi tehdä kahdella vaihtoehtoisella tavalla: joko käyttämällä
f-stringejä, jotka ovat olleet käytössä Pythonin versiosta 3.6 lähtien
tai käyttämällä vanhempaa format
-käskyä. Tässä esitellään ensin
muotoilu f-stringien avulla ja myöhemmin format
-käskyn käyttö.
Kun halutaan vaikuttaa print
-käskyllä tulostettavan rivin
muotoiluun, kirjoitetaan
ennen muotoiltavan merkkijonon aloittavaa lainausmerkkiä
f-kirjain.
Sellaisen muuttujan nimi, jonka arvo halutaan liittää tulostettavaan
merkkijonoon, kirjoitetaan aaltosulkujen sisään. Muuttujan nimen jälkeen
samojen aaltosulkujen sisään lisätään muotoilumääre, joka kertoo, miten
tulostettava arvo muotoillaan, esimerkiksi kuinka leveä tulostuskenttä arvolle
varataan ja montako desimaalia desimaaliluvusta tulostetaan.
Muotoilumääre alkaa kaksoispisteellä :
. Sen jälkeen siinä kerrotaan
ensin arvolle varattavan kentän leveys kokonaislukuna. Leveys voidaan
jättää myös pois, jolloin arvolle varataan sen tarvitsema tila
(esimerkiksi 4-numeroiselle kokonaisluvulle 4 merkin levyinen kenttä).
Desimaalilukuja koskevissa muotoilumääreissä tulee seuraavaksi piste ja
tämän jälkeen toinen luku, joka kertoo, kuinka monta numeroa esitetään
desimaalipisteen jälkeen. Viimeisenä muotoilumääressä on
määrittelykirjain, joka kertoo, minkä tyyppinen arvo kohtaan tulee,
esimerkiksi d
, kun on kysymyksessä kokonaisluku, s
, jos
kysymyksessä on merkkijono ja f
, e
tai g
, jos kysymyksessä
on desimaaliluku. (Muotoilumääreissä voi olla myös muita osia tai muita
kirjaimia erilaisia tyyppejä varten. Niitä ei esitetä tässä sen
tarkemmin, mutta kiinnostunut lukija voi tutkia asiaa tarkemmin Pythonin
omasta dokumentaatiosta.)
Seuraavaksi esitellään joitakin esimerkkejä muotoilumääreiden käytöstä ja esimerkkikäskyjen tulostuksesta.
sivu = 4.5648945
print(f"Sivun pituus on {sivu:5.2f} m.")
Tulostus
Sivun pituus on 4.56 m.
Merkkijonon sisällä on aaltosulkujen ja niiden sisällä olevien merkintöjen
avulla määrätty, että aaltosulkujen kohdalle tulostettavaan merkkijonoon
tulee muuttujan sivu
float-tyyppinen arvo niin, että sille varataan viiden merkin levyinen
kenttä, ja luvusta esitetään kaksi desimaalia. Tulostetun luvun edessä on
yhden välilyönnin sijaan kaksi välilyöntiä siksi, että tulostettavalle
luvulle on varattu viiden merkin levyinen tila, mutta luku vie vain
neljä merkkiä.
Tulostettavassa merkkijonossa voi olla myös useita eri arvoja, joilla on jokaisella oma muotoilumääre, esimerkiksi:
kertoja = 5
kerrottava = 8
tulos = kertoja * kerrottava
print(f"{kertoja:3d} kertaa {kerrottava:3d} on {tulos:6d}")
Tulostus
5 kertaa 8 on 40
Tässä on kerrottu, että tulostettavaan tekstiin tulee ensin
muuttujan kerrottava
kokonaislukuarvo, jolle varataan 3 merkin levyinen kenttä,
sen jälkeen
teksti “ kertaa ”, sitten muuttujan kerrottava``kokonaislukuarvo, jolle varataan 3 merkin
levyinen kenttä, sen jälkeen teksti “ on ” ja lopuksi muuttujan ``tulos
kokonaislukuarvo,
jolle varataan kuuden merkin levyinen kenttä.
Seuraavassa esimerkissä määritellään muotoilumääreiden avulla merkkijonojen ja kokonaisluvun kentän leveys. Näin peräkkäin tulostettavilla riveillä sarakkeet menevät päällekkäin, vaikka merkkijonojen pituus ja lukujen suuruus vaihtelevat
enimi1 = "Matti"
snimi1 = "Virtanen"
palkka1 = 2800
enimi2 = "Tiina-Maija"
snimi2 = "Pomo"
palkka2 = 11000
print(f"{snimi1:15s} {enimi1:15s} {palkka1:5d} euroa")
print(f"{snimi2:15s} {enimi2:15s} {palkka2:5d} euroa")
Tulostus
Virtanen Matti 2800 euroa
Pomo Tiina-Maija 11000 euroa
Oletuksen mukaisesti merkkijonoja sisältävät
sarakkeet on tasattu niin, että niiden vasen reuna on samassa kohdassa,
ja lukuja sisältävät sarakkeet niin, että niiden oikea reuna on samassa
kohdassa. Jos halutaan säätää lukujen tulostus kentän vasempaan reunaan,
lisätään muotoilumääreeseen :
-merkin jälkeen merkki <
, ja jos
halutaan säätää merkkijonojen tulostus kentän oikeaan reunaan, lisätään
muotoilumääreeseen :
-merkin jälkeen merkki >
. Ylänuolen ^
avulla
voi säätää tulostuksen kentän keskelle. Alla on
esimerkkejä paikan säätämisestä:
print(f"{snimi1:>15s} {enimi1:>15s} {palkka1:<5d} euroa")
print(f"{snimi2:>15s} {enimi2:>15s} {palkka2:<5d} euroa")
Tulostaa (huomaa ylimääräiset välilyönnit rivin alussa)
Virtanen Matti 2800 euroa
Pomo Tiina-Maija 11000 euroa
Seuraavaksi on vielä joitain esimerkkejä desimaalilukujen muotoilusta.
Ensimmäisessä
esimerkissä luvut on muotoiltu määrittelykirjaimen f
avulla.
sivu = 4.5648945
pinta_ala = sivu * sivu
print(f"Sivu on {sivu:5.2f} m ja pinta-ala {pinta_ala:8.2f} m2")
Tulostus:
Sivu on 4.56 m ja pinta-ala 20.84 m2
Jos muotoilumääreessä ei anneta kentän leveyttä, luvulle varataan tarpeellinen määrä tilaa. Muotoilumääreellä voi tällöinkin vaikuttaa esimerkiksi siihen, kuinka monta desimaalia desimaaliluvusta tulostetaan. Useimmissa harjoitustehtävissä on tarkoitus käyttää tätä tapaa silloin, kun luvut pyydetään tulostamaan määrätyn desimaalimäärän tarkkuudella.
print(f"Sivu on {sivu:.2f} m ja pinta-ala {pinta_ala:.2f} m2")
Tulostus:
Sivu on 4.56 m ja pinta-ala 20.84 m2
Seuraavassa esimerkissä samat luvut on tulostettu määrittelykirjaimen
e
avulla. Tällöin desimaaliluku esitetään niin, että
desimaalipisteen edessä on vain yksi numero ja lisäksi kerrotaan, millä
kymmenen eksponentilla luku pitää kertoa. Esimerkiksi 5.87e+01
tarkoittaa \(5.87 \cdot {10}^1\). Ensimmäinen luku ei enää mahdu
sille varattuun kenttään, mutta kuten esimerkistä nähdään, kentän
leveyttä kasvatetaan tarvittaessa muotoilukoodissa annettua leveyttä
suuremmaksi.
print(f"Sivu on {sivu:5.2e} m ja pinta-ala {pinta_ala:8.2e} m2")
Tulostus:
Sivu on 4.56e+00 m ja pinta-ala 2.08e+01 m2
Jos ei ole tietoa tulostettavan desimaaliluvun suuruusluokasta, voi
käyttää määrittelykirjainta g
. Tällöin luku tulostetaan ilman
eksponenttiosaa, jos näin voidaan siististi tehdä, ja eksponenttiosan
kanssa, jos luku on itseisarvoltaan hyvin pieni tai suuri.
Määrittelykirjainta g
käytettäessä pisteen jälkeen tuleva luku ei
kuitenkaan enää määrää desimaalipisteen jälkeen tulevien lukujen
lukumäärää, vaan tulostuksessa esiintyvien merkitsevien numeroiden
lukumäärää.
pieni_luku = 1.2308e01
suuri_luku = 14.5e20
tulo = pieni_luku * suuri_luku
print(f"Kun kerrotaan {pieni_luku:5.4g} ja {suuri_luku:8.4g}, saadaan {tulo:10.4g}")
Tulostus:
Kun kerrotaan 12.31 ja 1.45e+21, saadaan 1.785e+22
Tulostusta muotoiltaessa tulostettavan arvon ei tarvitse olla suoraan muuttujan arvona, vaan aaltosulkujen sisällä voidaan antaa tulostettavaksi mitä tahansa lausekkeita, joiden arvon voi laskea, esimerkiksi
korko = 2.5
paaoma = 12000
print(f"Saat korkoa {korko / 100.0 * paaoma:.2f} eur".format(korko / 100.0 * paaoma))
Tulostus:
Saat korkoa 300.00 eur
Tulostuksessa voi käyttää hyväksi f-stringejä myös silloin, kun tulostettavia arvoja ei halua varsinaisesti muotoilla. Näin saa helposti esimerkiksi välimerkit tulostettavien arvojen eteen tai jälkeen ilman ylimääräisiä välilyöntejä.
luku = 5
potenssi = 4
print(f"Jos {luku} korotetaan potenssiin {potenssi}, saadaan {luku ** potenssi}.")
Tulostus:
Jos 5 korotetaan potenssiin 4, saadaan 625.
Alla olevaan taulukkoon on vielä koottu tärkeimmät muotoilussa käytettävät määrittelykirjaimet. Taulukko ei ole täydellinen, vaan siihen on otettu mukaan vain tällä kurssilla käytetyt ja tarvittavat kirjaimet.
Kirjain | Käyttötarkoitus |
s | merkkijonot |
d | kokonaisluvut |
f | desimaaliluvut ilman eksponenttimerkintää |
e | desimaaliluvut eksponenttimerkinnän e kanssa |
E | desimaaliluvut eksponenttimerkinnän E kanssa |
g | desimaaliluvut joko eksponenttimerkinnän e kanssa tai ilman suuruusluokan mukaan |
G | desimaaliluvut joko eksponenttimerkinnän E kanssa tai ilman suuruusluokan mukaan |
Toiseen taulukkoon on koottu merkit, joilla voi vaikuttaa arvon sijoitteluun sille varatun kentän (käytettävän tilan) sisällä:
Merkki | Vaikutus |
< | tasaa arvon kentän vasempaan reunaan |
> | tasaa arvon kentän oikeaan reunaan |
^ | sijoittaa arvon kentän keskelle |
Vanhempi tapa: käsky format
Pythonin versiota 3.6 vanhemmissa versioissa ei ollut käytössä f-stringejä,
vaan tulostuksen (täsmällisemmin tulostettavan merkkijonon) muotoilu voitiin
tehdä format
-käskyn avulla. Tätä käskyä voi edelleen käyttää Pythonin
uudemmissakin versioissa f-stringien sijaan. Käskyn käyttäminen ei ole
tällä kurssilla tarpeellista, mutta se kuitenkin esitellään tässä lyhyesti,
jotta lukijalla olisi mahdollista ymmärtää muiden kirjoittamia ohjelmia,
joissa tulostuksen muotoilu on tehty tällä vanhemmalla tavalla.
Tässäkin tavassa merkitään aaltosulkujen avulla merkkijonon sisällä paikkoja, joihin tulee tulostuksessa jonkin muuttujan tai muun laskettavan lausekkeen arvo. Mutta aaltosulkujen sisälle ei nyt kirjoiteta muuttujan nimeä, vaan pelkästään muotoilumääre, joka on samanlainen kuin f-stringejä käytettäessä. Se siis alkaa kaksoispisteellä, jonka jälkeen tulee arvolle varattavan kentän leveys, mahdollisesti muita tietoja ja lopuksi määrittelykirjain.
Lainausmerkeissä olevan tekstin jälkeen kirjoitetaan piste, käsky
format
, ja sen jälkeen luetellaan sulkujen sisällä niiden muuttujien
nimet, joiden arvot rivillä halutaan tulostaa. Riviä tulostettaessa
aaltosulut ja niiden sisällä olevat muotoilumääreet korvataan
luettelossa olevien muuttujien arvoilla niin, että ensimmäiset
aaltosulut sisältöineen korvataan luettelon ensimmäisen muuttujan
arvolla, toiset aaltosulut sisältöineen luettelon toisen muuttujan
arvolla jne. Alla on sama esimerkki kuin ylempänä f-stringien kohdalla,
mutta muotoilu on nyt tehty format
-käskyn avulla:
kertoja = 5
kerrottava = 8
tulos = kertoja * kerrottava
print("{:3d} kertaa {:3d} on {:6d}".format(kertoja, kerrottava, tulos))
Tulostus:
5 kertaa 8 on 40
Seuraavassa esimerkissä määrätään tulostettavien desimaalilukujen tarkkuus, mutta ei määrätä kentän leveyttä:
sivu = 4.5648945
pinta_ala = sivu * sivu
print("Sivu on {:.2f} m ja pinta-ala {:.2f} m2".format(sivu, pinta_ala))
Tulostus:
Sivu on 4.56 m ja pinta-ala 20.84 m2
Myös kaikki muut aikaisemmin f-stringien avulla esitetyt esimerkit voidaan
muuttaa käyttämään ``format``-käskyä.
*Huomautus:* oikeastaan muotoilussa ``format``-käskyn avulla on kysymys siitä, että
print
-käskyssä annetulle merkkijonolle kutsutaan format
-nimistä
metodia, joka suorittaa tulostettavan merkkijonon muotoilun. Tätä ei
kuitenkaan tarvitse vielä ymmärtää tässä vaiheessa, vaan asiaan palataan
sitten, kun käsitellään olioita.
Tämän kurssimateriaalin esimerkeissä käytetään seuraavissa luvuissa
f-stringejä, mutta opiskelija voi harjoitustehtävissä käyttää halutessaan
muotoiluun myös format
-käskyä.