7. Vstup a výstup¶
Výstup programu lze prezentovat několika způsoby. Data mohou být vypsána ve formě čitelné pro člověka nebo zapsána do souboru pro pozdější použití. Tato kapitola se zabývá některými z těchto možností.
7.1. Pokročilejší formátování výstupu¶
Dosud jsme se setkali se dvěma způsoby výpisu hodnot: výrazovými příkazy a
funkcí print(). (Třetím způsobem je použití metody
write() souborových objektů; na soubor standardního výstupu
lze odkazovat jako na sys.stdout. Další informace naleznete v referenční
příručce knihovny.)
Často budete chtít mít nad formátováním výstupu větší kontrolu než při pouhém výpisu hodnot oddělených mezerami. Výstup lze formátovat několika způsoby.
Chcete-li použít formátované řetězcové literály, začněte řetězec znakem
fneboFpřed úvodní uvozovkou či trojicí uvozovek. Uvnitř řetězce můžete mezi znaky{a}zapsat výraz Pythonu, který může odkazovat na proměnné nebo literálové hodnoty.>>> year = 2016 >>> event = 'Referendum' >>> f'Results of the {year} {event}' 'Results of the 2016 Referendum'
Metoda řetězců
str.format()vyžaduje více ruční práce. Znaky{a}nadále označují místo, kam bude dosazena proměnná, a umožňují zadat podrobné formátovací pokyny, musíte však také dodat formátované údaje. Následující blok kódu obsahuje dva příklady formátování proměnných:>>> yes_votes = 42_572_654 >>> total_votes = 85_705_149 >>> percentage = yes_votes / total_votes >>> '{:-9} YES votes {:2.2%}'.format(yes_votes, percentage) ' 42572654 YES votes 49.67%'
Všimněte si, že hodnota
yes_votesje doplněna mezerami a znaménko minus se zobrazí pouze u záporných čísel. Příklad také vypíše hodnotupercentagevynásobenou 100, se dvěma desetinnými místy a následovanou znakem procenta (podrobnosti viz Format specification mini-language).Nakonec můžete veškeré zpracování řetězců provést sami pomocí vytváření výřezů a zřetězení a vytvořit libovolné rozvržení. Řetězcový typ nabízí také několik užitečných metod pro doplnění řetězců na danou šířku sloupce.
Pokud nepotřebujete propracovaný výstup, ale chcete pouze rychle zobrazit
některé proměnné při ladění, můžete libovolnou hodnotu převést na řetězec pomocí
funkcí repr() nebo str().
Funkce str() má vracet reprezentace hodnot, které jsou poměrně dobře
čitelné pro člověka, zatímco repr() má vytvářet reprezentace čitelné
interpretem (nebo způsobit SyntaxError, pokud odpovídající syntaxe
neexistuje). U objektů bez zvláštní reprezentace určené pro člověka vrátí
str() stejnou hodnotu jako repr(). Mnoho hodnot, například čísla
nebo struktury jako seznamy a slovníky, má při použití obou funkcí stejnou
reprezentaci. Zejména řetězce mají dvě odlišné reprezentace.
Několik příkladů:
>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> str(1/7)
'0.14285714285714285'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
>>> print(s)
The value of x is 32.5, and y is 40000...
>>> # The repr() of a string adds string quotes and backslashes:
>>> hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print(hellos)
'hello, world\n'
>>> # The argument to repr() may be any Python object:
>>> repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"
Modul string prostřednictvím třídy string.Template podporuje
jednoduchý způsob vytváření šablon založený na regulárních výrazech. Nabízí tak
další způsob dosazování hodnot do řetězců, který používá zástupné symboly jako
$x a nahrazuje je hodnotami ze slovníku. Tato syntaxe se snadno používá,
nabízí však podstatně menší kontrolu nad formátováním.
7.1.1. Formátované řetězcové literály¶
Formátované řetězcové literály (zkráceně také f-řetězce)
umožňují vložit hodnotu výrazů Pythonu do řetězce. Řetězci se přidá prefix f
nebo F a výrazy se zapisují jako {expression}.
Za výrazem může následovat nepovinný specifikátor formátu. Ten poskytuje větší kontrolu nad způsobem formátování hodnoty. Následující příklad zaokrouhlí číslo pí na tři desetinná místa:
>>> import math
>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.
Celé číslo uvedené za ':' určuje minimální šířku daného pole ve znacích.
To je užitečné pro zarovnání sloupců.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print(f'{name:10} ==> {phone:10d}')
...
Sjoerd ==> 4127
Jack ==> 4098
Dcab ==> 7678
Jiné modifikátory lze použít k převodu hodnoty před jejím formátováním.
'!a' použije ascii(), '!s' použije str() a '!r' použije
repr():
>>> animals = 'eels'
>>> print(f'My hovercraft is full of {animals}.')
My hovercraft is full of eels.
>>> print(f'My hovercraft is full of {animals!r}.')
My hovercraft is full of 'eels'.
Specifikátor = lze použít k rozvinutí výrazu na jeho text, znak rovnosti a
následně reprezentaci vyhodnoceného výrazu:
>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'
Další informace o specifikátoru = naleznete v části
samodokumentující výrazy. Přehled těchto
formátovacích specifikací obsahuje referenční příručka Format specification mini-language.
7.1.2. Řetězcová metoda format()¶
Základní použití metody str.format() vypadá takto:
>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"
Složené závorky a znaky v nich (nazývané formátovací pole) jsou nahrazeny
objekty předanými metodě str.format(). Číslem v závorkách lze odkázat na
pozici objektu předaného metodě str.format().
>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam
Pokud metoda str.format() používá argumenty klíčových slov, na jejich
hodnoty se odkazuje názvem argumentu.
>>> print('This {food} is {adjective}.'.format(
... food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.
Poziční argumenty a argumenty klíčových slov lze libovolně kombinovat:
>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
... other='Georg'))
The story of Bill, Manfred, and Georg.
Máte-li velmi dlouhý formátovací řetězec, který nechcete rozdělovat, je vhodné
odkazovat na formátované proměnné názvem namísto jejich pozice. Stačí předat
slovník a k přístupu ke klíčům použít hranaté závorky '[]'.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
... 'Dcab: {0[Dcab]:d}'.format(table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678
Totéž lze provést předáním slovníku table jako argumentů klíčových slov
pomocí zápisu **.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678
To je zvláště užitečné v kombinaci s vestavěnou funkcí vars(), která vrací
slovník obsahující všechny lokální proměnné:
>>> table = {k: str(v) for k, v in vars().items()}
>>> message = " ".join([f'{k}: ' + '{' + k +'};' for k in table.keys()])
>>> print(message.format(**table))
__name__: __main__; __doc__: None; __package__: None; __loader__: ...
Následující řádky například vytvoří úhledně zarovnané sloupce celých čísel, jejich druhých a třetích mocnin:
>>> for x in range(1, 11):
... print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
Úplný přehled formátování řetězců pomocí str.format() naleznete v části
Format string syntax.
7.1.3. Ruční formátování řetězců¶
Zde je stejná tabulka druhých a třetích mocnin, tentokrát formátovaná ručně:
>>> for x in range(1, 11):
... print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
... # Note use of 'end' on previous line
... print(repr(x*x*x).rjust(4))
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
(Všimněte si, že jednu mezeru mezi jednotlivými sloupci přidala sama funkce
print(), která mezi své argumenty vždy vkládá mezery.)
Metoda řetězcových objektů str.rjust() zarovná řetězec doprava v poli
zadané šířky tím, že jej zleva doplní mezerami. Podobně fungují metody
str.ljust() a str.center(). Tyto metody nic nevypisují, pouze vracejí
nový řetězec. Je-li vstupní řetězec příliš dlouhý, nezkrátí jej, ale vrátí beze
změny. Rozvržení sloupců se tím sice poruší, obvykle je to však lepší než
zkreslit hodnotu. (Pokud řetězec opravdu chcete zkrátit, můžete vždy přidat
vytvoření výřezu, například x.ljust(n)[:n].)
Další metoda str.zfill() doplní číselný řetězec zleva nulami. Bere přitom
v úvahu znaménka plus a minus:
>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'
7.1.4. Starý způsob formátování řetězců¶
K formátování řetězců lze použít také operátor % (modulo). V zápisu
format % values (kde format je řetězec) se převodní specifikace % v
řetězci format nahradí žádným, jedním nebo více prvky z values. Tato operace
se běžně označuje jako interpolace řetězců. Například:
>>> import math
>>> print('The value of pi is approximately %5.3f.' % math.pi)
The value of pi is approximately 3.142.
Další informace naleznete v části printf-style String Formatting.
7.2. Čtení a zápis souborů¶
open() vrací souborový objekt a nejčastěji se
používá se dvěma pozičními argumenty a jedním argumentem klíčového slova:
open(filename, mode, encoding=None)
>>> f = open('workfile', 'w', encoding="utf-8")
První argument je řetězec obsahující název souboru. Druhý argument je další
řetězec s několika znaky popisujícími způsob použití souboru. mode může být
'r', pokud se soubor bude pouze číst, 'w' pro pouhý zápis (existující
soubor stejného názvu bude vymazán) a 'a' otevře soubor pro přidávání;
všechna zapsaná data se automaticky připojí na konec. 'r+' otevře soubor
pro čtení i zápis. Argument mode je nepovinný; pokud jej vynecháte,
předpokládá se 'r'.
Soubory se obvykle otevírají v textovém režimu <text mode>. To znamená,
že se z nich čtou a zapisují se do nich řetězce kódované pomocí určitého
kódování (encoding). Není-li encoding uvedeno, výchozí hodnota závisí na
platformě (viz open()). Protože UTF-8 je moderním standardem de facto,
doporučuje se používat encoding="utf-8", pokud nevíte, že potřebujete jiné
kódování. Přidáním 'b' k režimu se soubor otevře v binárním režimu
<binary mode>. Data se v binárním režimu čtou a zapisují jako objekty
bytes. Při otevření souboru v binárním režimu nelze encoding zadat.
Při čtení v textovém režimu se konce řádků specifické pro platformu (\n v
Unixu, \r\n ve Windows) standardně převádějí na pouhé \n. Při zápisu v
textovém režimu se výskyty \n standardně převádějí zpět na konce řádků
dané platformy. Tato skrytá úprava souborových dat je vhodná pro textové
soubory, poškodila by však binární data, například v souborech JPEG
nebo EXE. Při čtení a zápisu takových souborů proto vždy pečlivě
používejte binární režim.
Při práci se souborovými objekty je vhodné používat klíčové slovo
with. Výhodou je, že se soubor po dokončení bloku správně zavře, i
když se v některém okamžiku vyvolá výjimka. Použití with je také
podstatně kratší než zápis odpovídajících bloků
try-finally:
>>> with open('workfile', encoding="utf-8") as f:
... read_data = f.read()
>>> # We can check that the file has been automatically closed.
>>> f.closed
True
Pokud klíčové slovo with nepoužíváte, měli byste voláním
f.close() soubor zavřít a okamžitě uvolnit všechny systémové prostředky,
které využívá.
Varování
Volání f.write() bez použití klíčového slova with nebo bez
volání f.close() může způsobit, že argumenty funkce f.write()
nebudou zcela zapsány na disk, přestože program úspěšně skončí.
Po zavření souborového objektu, ať už příkazem with, nebo voláním
f.close(), všechny pokusy o použití tohoto objektu automaticky selžou.
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
7.2.1. Metody souborových objektů¶
Ve zbývajících příkladech této části se předpokládá, že již byl vytvořen
souborový objekt s názvem f.
Obsah souboru se čte voláním f.read(size), které načte určité množství dat
a vrátí je jako řetězec (v textovém režimu) nebo objekt typu bytes (v binárním
režimu). size je nepovinný číselný argument. Pokud se size vynechá nebo je
záporné, načte a vrátí se celý obsah souboru; pokud je soubor dvakrát větší než
paměť počítače, je to váš problém. Jinak se načte a vrátí nejvýše size znaků
(v textovém režimu) nebo size bajtů (v binárním režimu). Po dosažení konce
souboru vrátí f.read() prázdný řetězec ('').
>>> f.read()
'This is the entire file.\n'
>>> f.read()
''
f.readline() načte ze souboru jeden řádek. Znak nového řádku (\n)
zůstává na konci řetězce a vynechá se pouze na posledním řádku souboru, pokud
soubor znakem nového řádku nekončí. Návratová hodnota je díky tomu jednoznačná:
vrátí-li f.readline() prázdný řetězec, bylo dosaženo konce souboru, zatímco
prázdný řádek je reprezentován řetězcem '\n' obsahujícím jediný znak nového
řádku.
>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''
Při čtení řádků ze souboru lze procházet souborový objekt cyklem. Tento způsob je paměťově úsporný, rychlý a vede k jednoduchému kódu:
>>> for line in f:
... print(line, end='')
...
This is the first line of the file.
Second line of the file
Chcete-li načíst všechny řádky souboru do seznamu, můžete také použít
list(f) nebo f.readlines().
f.write(string) zapíše obsah string do souboru a vrátí počet zapsaných
znaků.
>>> f.write('This is a test\n')
15
Objekty jiných typů je nutné před zápisem převést – buď na řetězec (v textovém
režimu), nebo na objekt typu bytes (v binárním režimu):
>>> value = ('the answer', 42)
>>> s = str(value) # convert the tuple to string
>>> f.write(s)
18
f.tell() vrátí celé číslo udávající aktuální pozici souborového objektu v
souboru. V binárním režimu je vyjádřena jako počet bajtů od začátku souboru, v
textovém režimu jako abstraktní číslo.
Pozice souborového objektu se mění pomocí f.seek(offset, whence). Vypočítá
se přičtením offset k referenčnímu bodu, který určuje argument whence.
Hodnota whence 0 počítá od začátku souboru, 1 používá aktuální pozici v
souboru a 2 používá jako referenční bod konec souboru. whence lze vynechat;
výchozí hodnotou je 0, tedy referenční bod na začátku souboru.
>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5) # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'
V textových souborech (otevřených bez b v řetězci režimu) jsou povoleny
pouze přesuny relativně k začátku souboru. Výjimkou je přesun přímo na konec
souboru pomocí seek(0, 2). Jedinými platnými hodnotami offset jsou hodnoty
vrácené funkcí f.tell() nebo nula. Jakákoli jiná hodnota offset vede k
nedefinovanému chování.
Souborové objekty mají několik dalších, méně často používaných metod, například
isatty() a truncate(). Úplného průvodce
souborovými objekty naleznete v referenční příručce knihovny.
7.2.2. Ukládání strukturovaných dat pomocí json¶
Řetězce lze snadno zapisovat do souboru a opět z něj číst. Čísla vyžadují o
něco více práce, protože metoda read() vrací pouze řetězce.
Ty je třeba předat například funkci int(), která přijme řetězec jako
'123' a vrátí jeho číselnou hodnotu 123. Chcete-li ukládat složitější datové
typy, například vnořené seznamy a slovníky, ruční analýza a serializace se
stávají komplikovanými.
Aby uživatelé nemuseli neustále psát a ladit kód pro ukládání složitých datových
typů do souborů, umožňuje Python použít oblíbený formát pro výměnu dat nazvaný
JSON (JavaScript Object Notation). Standardní modul
json dokáže převést datové hierarchie Pythonu na řetězcové reprezentace;
tento proces se nazývá serializace <serializing>. Obnovení dat z
řetězcové reprezentace se nazývá deserializace <deserializing>. Mezi
serializací a deserializací může být řetězec reprezentující objekt uložen v
souboru či datech nebo odeslán síťovým spojením na vzdálený počítač.
Poznámka
Formát JSON moderní aplikace běžně používají k výměně dat. Mnoho programátorů jej již zná, a proto je dobrou volbou pro interoperabilitu.
Máte-li objekt x, můžete jeho řetězcovou reprezentaci JSON zobrazit jediným
řádkem kódu:
>>> import json
>>> x = [1, 'simple', 'list']
>>> json.dumps(x)
'[1, "simple", "list"]'
Jiná varianta funkce dumps(), nazvaná dump(), jednoduše
serializuje objekt do textového souboru. Je-li tedy f
objekt textového souboru otevřený pro zápis, můžeme provést
následující:
json.dump(x, f)
Chceme-li objekt znovu dekódovat a f je objekt binárního souboru nebo textového souboru otevřený pro čtení:
x = json.load(f)
Poznámka
Soubory JSON musí být kódovány v UTF-8. Při otevírání souboru JSON jako
textového souboru pro čtení i zápis použijte
encoding="utf-8".
Tato jednoduchá serializační technika zvládá seznamy a slovníky, ale
serializace libovolných instancí tříd do JSON vyžaduje trochu práce navíc.
Vysvětlení obsahuje referenční dokumentace modulu json.
Viz také
pickle - modul pickle
Na rozdíl od JSON je pickle protokol umožňující serializaci libovolně složitých objektů Pythonu. Je proto specifický pro Python a nelze jej použít ke komunikaci s aplikacemi napsanými v jiných jazycích. Ve výchozím nastavení také není bezpečný: deserializace dat pickle z nedůvěryhodného zdroje může spustit libovolný kód, pokud data připravil zkušený útočník.