Cvičení č. 13 rozšířené o poznámky ze cvičení a řešení některých příkladů (ZS 2024/25)¶
V minulém díle jste viděli…¶
Základní kontejnery:
str
(string neboli textový řetězec) – posloupnost znakůlist
(seznam) – modifikovatelná (mutable) posloupnost libovolných objektůtuple
(n-tice) – nemodifikovatelná (immutable) posloupnost libovolných objektůset
(množina) – datová struktura obsahující unikátní prvky, pro které není určené jejich vzájemné pořadídict
(slovník) – datová struktura, která představuje zobrazení z množiny klíčů (keys) na libovolné hodnoty (values)
Základní operace s kontejnery:
- testování:
var in container
– výraz s hodnotouTrue
/False
- iterování:
for var in container
Také vždy můžeme použít funkci len(container)
, která vrací délku neboli počet prvků kontejneru.
Pro zápis seznamů se používají hranaté závorky: [1, 2, 3]
a pro zápis n-tic se používají kulaté závorky: (1, 2, 3)
.
Pro zápis množin i slovníků se v kódu používají složené závorky. Množina obsahuje přímo své prvky oddělené čárkou, kdežto slovník obsahuje dvojice klíč-hodnota oddělené dvojtečkou:
my_set = {1, 2, 3}
my_dict = {1: "a", 2: "b", 3: "c"}
Prázdné složené závorky nedefinují prázdnou množinu, ale prázdný slovník:
var = {}
print(type(var))
var = set()
print(type(var))
var = str()
print(var)
var = ""
print(var)
print(type(var))
<class 'dict'> <class 'set'> <class 'str'>
# stringy jsou posloupnosti, proto m;6e
my_string = "abcdefgh ijkl"
print(len(my_string))
# podmínka
if "a" in my_string:
print("je tam")
# cyklus
for x in my_string:
print(x, end = " ")
# porovnání
if my_string != "ahoj":
print("\nNejsou stejné.")
# indexace a slicing
print(my_string[0])
print(my_string[1:4])
#my_string[0] = 'b' # chyba
13 je tam a b c d e f g h i j k l Nejsou stejné. a bcd
Při používání modifikovatelných objektů je potřeba rozlišovat mezi identitou a hodnotou různých proměnných: identita představuje umístění objektu v paměti počítače, kdežto hodnota jen data uložená v dané proměnné. Proč je to důležité:
- Přiřazovací operátor (např.
a = b
) pouze nastaví odkaz na stejný objekt. (Umíme ale vytvořit skutečnou kopii kontejneru – mělkou nebo hlubokou.) - Předávání parametrů funkcím také předá jen odkaz na daný objekt, takže změny modifikovatelných parametrů uvnitř funkce se projeví i mimo funkci.
Textové řetězce¶
Textový řetězec (anglicky a slangově string) je posloupnost znaků.
V Pythonu jsou textové řetězce reprezentovány pomocí typu str
, což je nemodifikovatelný (immutable) kontejner.
Python na rozdíl od jiných programovacích jazyků nezavádí samostatný datový typ pro znaky (jako např. char
v jazyce C/C++).
Znakem v Pythonu rozumíme string obsahující pouze jeden znak.
Interně jsou stringy uloženy pomocí typu bytes
(také nemodifikovatelná posloupnost) a nějakého kódování (typicky UTF-8).
Zápis řetězců a escape sekvence¶
Pro zápis řetězců v kódu programu používáme nejčastěji jednoduché uvozovky (apostrofy) nebo dvojité uvozovky:
prvni_prazdny_textovy_retezec = ""
druhy_prazdny_textovy_retezec = ''
treti_prazdny = str()
print(prvni_prazdny_textovy_retezec, druhy_prazdny_textovy_retezec, treti_prazdny)
prvni_neprazdny_textovy_retezec = "Python"
druhy_neprazdny_textovy_retezec = 'C++'
Význam použitých uvozovek se nijak neliší: podobně jako 1.0
a 1.000
jsou dva zápisy téhož čísla, tak i 'slovo'
a "slovo"
jsou v Pythonu dva zápisy téhož stringu.
Použité uvozovky nejsou součástí hodnoty – Python si "nepamatuje", jakým způsobem byl řetězec uvozen.
Volba hraničních znaků může být ovlivněna tím, zda potřebujeme některý z nich použít uprostřed textu:
s1 = "Potřebuji 'apostrofy'"
s2 = 'Potřebuji "uvozovky"'
print(s1)
print(s2)
Potřebuji 'apostrofy' Potřebuji "uvozovky"
V případě, že potřebujeme vytvořit string obsahující nějaké speciální znaky, můžeme použit tzv. escapování.
Escapování znaků znamená, že před nějakým znakem napíšeme zpětné lomítko (\
), kterým začíná speciální posloupnost znaků s jiným významem – tzv. escape sekvence (anglicky escape sequence).
Tuto sekvenci Python interpretuje jako jediný speciální znak.
Některé speciální znaky nejsou "viditelné", ale mohou např. vyvolat nějakou akci terminálu.
Např. znak \n
znamená ukončení řádku:
print("Několika řádkový\ntext")
print("Několika řádkový\ttext")
print("Několika řádkový\"text")
print("Několika řádkový'text")
print("Několika řádkový\'text")
print("Několika řádkový\\\text")
Několika řádkový text Několika řádkový text Několika řádkový"text Několika řádkový'text Několika řádkový'text Několika řádkový\ ext
Escapování se také často používá pro zápis jednoduchých a dvojitých uvozovek uvnitř stringu: \'
, \"
.
print("Teď potřebuji \'apostrofy\' i \"uvozovky\"")
print('Teď potřebuji \'apostrofy\' i \"uvozovky\"')
Teď potřebuji 'apostrofy' i "uvozovky" Teď potřebuji 'apostrofy' i "uvozovky"
Pokud chceme zapsat string obsahující zpětné lomítko, musíme ho escapovat jako \\
.
Např. pro vytvoření cesty k adresáři v operačním systému Windows:
print("C:\\ZPRO\\Nový adresář")
C:\ZPRO\Nový adresář
Tyto a některé další příklady escape sekvencí v Pythonu jsou v tabulce níže.
Sekvence | Krátký popis |
---|---|
\' |
Jednoduché uvozovky nebo apostrof (kód \x27 ). |
\" |
Dvojité uvozovky (kód \x22 ). |
\\ |
Zpětné lomítko (kód \x5c ). |
\n |
Nový řádek (New Line – NL – kód \x0a ). |
\t |
Vodorovný tabulátor (TAB nebo HT – kód \x09 ). |
\v |
Svislý tabulátor (Vertical Tab – VT – kód \x0b ). |
\b |
Backspace (BS – smazání předchozího znaku – kód \x08 ). |
\r |
Návrat vozíku (Carriage Return – CR – kód \x0d ). |
\ooo |
Znak s 8bitovým kódem zadaným v osmičkové soustavě, o představuje osmičkovou číslici – např. '\101' == 'A' . |
\xhh |
Znak s 8bitovým kódem zadaným v šestnáctkové soustavě, h představuje šestnáctkovou číslici – např. '\x42' == 'B' . |
\uhhhh |
Znak s 16bitovým kódem zadaným v šestnáctkové soustavě, h představuje šestnáctkovou číslici – např. '\u263A' == '☺' . |
\Uhhhhhhhh |
Znak s 32bitovým kódem zadaným v šestnáctkové soustavě, h představuje šestnáctkovou číslici – např. '\U0000266b' == '♫' . |
\N{název} |
Znak, který má v sadě Unicode zadaný název – např. '\N{DEGREE CELSIUS}' == '\u2103' == '℃' nebo '\N{WHITE SMILING FACE}' == '\u263a' == '☺' . |
Jak je vidět z tabulky, se zpětným lomítkem se dá zadat jakýkoli znak – včetně emoji – podle názvu nebo kódu standardu Unicode. Stačí přesné jméno nebo číslo znát (nebo dohledat to na internetu). Jen pro zajímavost zkuste doplnit následující příklad použitím tabulátorů, \b
, \r
a výpisem nějakých "nestandardních" znaků sady Unicode.
print("A")
print("\x41")
print("\u0041")
print("\U00000041")
print("\xf3")
print("\u017E")
print("\N{DEGREE CELSIUS} \N{WHITE SMILING FACE}")
A A A A ó ž ℃ ☺
# ukázka navíc:
print(hex(193))
print(oct(193))
print(0xc1)
print(0o301)
print("\xc1") # 193
print("\u00c1") # 193
print("\U000000c1") # 193
print("\301")
0xc1 0o301 193 193 Á Á Á Á
print("\N{GREEK CAPITAL LETTER DELTA}")
print("\N{SECTION SIGN}")
print("\N{GRINNING CAT FACE WITH SMILING EYES}")
print("\u30C4")
Δ § 😸 ツ
Pro zápis víceřádkových stringů v kódu programu může být použití \n
příliš komplikované.
Alternativou je použít trojice dvojitých uvozovek ("""
) nebo jednoduchých uvozovek ('''
) pro ohraničení stringu:
s1 = "Jsem víceřádkový string\nobklopený dvojitými uvozovkami\na obsahující escapování pro nové řádky."
s2 = '''Jsem také víceřádkový string,
ale vytvořený pomocí
trojic jednoduchých uvozovek.'''
print(s1)
print(s2)
Jsem víceřádkový string obklopený dvojitými uvozovkami a obsahující escapování pro nové řádky. Jsem také víceřádkový string, ale vytvořený pomocí trojic jednoduchých uvozovek.
Převod stringu na číselné typy¶
S textovými řetězci jsme pracovali od prvního cvičení a mnoho operací se stringy již známe.
Umíme např. číst vstup od uživatele pomocí funkce input()
.
Připomeňme si, že tato funkce vždy vrací textový řetězec a v případě, že potřebujeme pracovat s číselnou hodnotou, musíme provést přetypovaní stringu na vhodný datový typ (např. pomocí funkcí int()
, float()
, nebo complex()
).
Ve funkci int(x, base=10)
se prvním argumentem předpokládá objekt, který lze převést na celé číslo.
Pokud jde o objekt typu str
, funkce int
požaduje, aby vyhovoval pravidlům pro zápis celého čísla v cílové číselné soustavě, navíc jsou povoleny pouze bílé znaky (mezera, tabulátor, nový řádek atd.) na začátku a na konci stringu (ne uvnitř) a podtržítka pro logické oddělení číslic uvnitř stringu.
Druhý argument base
(2 <= base <= 36
) určuje číselnou soustavu, do které se bude string převádět.
Číselné soustavy se základem větším než 10 používají v roli dalších číslic písmena A
–Z
, resp. a
–z
.
Na velikosti použitých písmen přitom nezáleží.
print(int(" 1010\t\n"))
print(int("1010", 2))
print(int("1010", 16))
print(int("No_nazdar", 36))
print(int("z", 36))
1010 10 4112 1856027718675 35
Funkce float(x=0.0)
vrátí "reálné" číslo vytvořené ze zadaného čísla či stringu.
Je-li argumentem string, funkce požaduje, aby vyhovoval pravidlům pro zápis reálných čísel.
Navíc jsou povoleny pouze bílé znaky na začátku a na konci stringu a několik speciálních "konstant".
Není-li zadán argument, funkce vrátí nulu.
print(float())
print(float("3_513"))
print(float(" -3.5\n"))
print(float("-Infinity")) # -Nekonečno, také jsou možné zkratky inf, -Inf, atd.
print(float("NaN")) # Not a Number - hodnota typu float, která není číslo
#print(float("Číslo 1.1")) # ValueError
0.0 3513.0 -3.5 -inf nan
Funkce complex(string)
vrací hodnotu typu complex
, pokud argument string
je korektní zápis komplexního čísla v algebraickém tvaru dle Pythonovské notace.
Mezery mezi reálnou a imaginární částmi nejsou povoleny a imaginární jednotku představuje symbol j
:
print(complex(" 1+2j"))
#print(complex("1 + 2j")) # ValueError
(1+2j)
Funkce bool(x=False)
převádí zadaný objekt x
na booleovskou hodnotu.
Pokud jde o typ str
, tak platí:
- prázdný řetězec se vyhodnotí jako
False
- jakýkoli neprázdný řetězec se vyhodnotí jako
True
print(bool(""))
print(bool("0"))
print(bool("False"))
False True True
Převod objektů na string¶
K převodu libovolného objektu na string slouží funkce str(object='')
.
Není-li zadán argument, funkce vrátí prázdný textový řetězec.
Existuje také funkce repr(object)
, která se snaží vytvořit jednoznačný textový podpis objektu – v řadě případů je jím text, po jehož zadání interpret vytvoří ekvivalentní objekt.
Jde o systémový popis objektu, používá se např. při výpisu výsledných hodnot v konzoli.
Funkce str()
naopak vrací "klasický" string, který by měl být pro člověka co nejsrozumitelnější – uživatelský podpis.
Návratové hodnoty funkcí str()
a repr()
jsou velmi často shodné, ale občas se liší.
Typickým příkladem jsou texty, pro něž funkce str()
vrací zadaný text, kdežto funkce repr()
vrací text uzavřený v uvozovkách, tj. text, který můžeme zadat do programu.
s = "abcd"
t = list(s)
print(s, t)
abcd ['a', 'b', 'c', 'd']
t = [1, 2]
u = str(t)
print(u, type(u))
print(u[0])
[1, 2] <class 'str'> [
print(str('Python'), str("Python"), str("""Python"""))
print(repr('Python'), repr("Python"), repr("""Python"""))
Python Python Python 'Python' 'Python' 'Python'
Základní funkce a operace s textovými řetězci¶
Typ str
je jeden ze základních kontejnerů v Pythonu a tedy můžeme použít všechny společné operace: testovací operátory in
a not in
(testují výskyt podřetězce) a iterování (for char in string
).
Délku stringu můžeme získat pomocí funkce len()
.
Dále typ str
představuje posloupnost, můžeme tedy používat indexování znaků ve stringu a slicing (viz cvičení 11).
Poznámka: my_string[i:j:k]
vybere každý k
-tý znak na pozicích od i
do j-1
včetně.
my_string = "Everybody loves ZPRO with Python!"
print(my_string[ : :2])
print(my_string[2:25:4])
print(my_string[-2:-8:-1])
EeyoylvsZR ihPto! eolsRi nohtyP
Sčítání a násobení¶
Pro textové řetězce můžeme používat "aritmetické" operátory +
a *
.
Sčítání stringů je implementováno jako jejich skládání nebo zřetězení (anglicky concatenation).
Při tom nezávisí na tom, jakým způsobem jsou jednotlivé sčítance zadány (tj. jaké uvozovky nebo escape sekvence jsou v kódu použity) – podstatná je jen hodnota jednotlivých stringů.
Navíc Python povoluje nevkládat operátor +
mezi dva stringy zadané pomocí uvozovek (nikoliv mezi uvozovky ohraničující string a např. nějakou proměnnou).
Na rozdíl od některých dalších programovacích jazyků, Python nepovoluje přičítat k textu něco jiného než text. Před přičtením hodnoty jiného druhu ve smyslu skládání musíme tuto hodnotu nejprve převést na text.
x = "abc"
y = "def"
x = x + y
print(x)
x += y
print(x)
y *= 4
print(y)
abcdef abcdefdef defdefdefdef
print("Jedna" + """Dva""" + 'Tři' '''Čtyři''')
print("Jedna: " + str(1))
#print("Jedna: " + 1) # TypeError
#print("Jedna" str(1)) # SyntaxError
JednaDvaTřiČtyři Jedna: 1
Operátor *
není definován pro dvojici stringů, ale jen pro string a celé číslo (int
).
Výsledkem bude text, v němž se textový činitel opakuje tolikrát, kolik je hodnota číselného činitele (na pořadí činitelů nezáleží).
print("abc" * 3)
print(2 * "abc")
#print(2.0 * "abc") # TypeError
abcabcabc abcabc
Funkce chr()
a ord()
¶
Funkce chr()
a ord()
jsou vzájemně inverzní funkce, které můžeme používat pro jednoznakové řetězce.
chr(i)
očekává celé číslo a vrátí string tvořený znakem s kódem i
.
Aby se znak zobrazil, musí být definován v použitém fontu, jinak se zobrazí jen zástupný symbol.
Funkce ord(c)
očekává string obsahující jeden znak a vrátí desítkový kód tohoto znaku ve znakové sadě Unicode.
print(chr(0x20ac)) # Znak euro
print(chr(0x1D120)) # Houslový klíč
print(ord("€"))
print(chr(8364))
print(chr(68))
print(ord('D'))
€ 𝄠 8364 € D 68
Porovnávání a řazení stringů¶
Řetězce lze porovnávat pomocí standardních operátorů ==
, !=
, <
, >
, <=
, >=
.
Porovnávání se uskutečňuje znak po znaku v pořadí zleva doprava podle kódů znaků se stejným indexem.
Jakmile znaky na stejných pozicích nebudou shodné, je za větší prohlášen ten text, v němž je znak s větším kódem.
Pokud už není co porovnávat, protože jeden ze stringů skončil, za větší je prohlášen ten druhý.
Poznámka: I když popsaný algoritmus vypadá velmi podobně lexikografickému uspořádání, není to přesně tak. Např. v případě použití písmen s diakritikou nebo nepísmenných znaků můžeme dostat vůbec neočekávaný výsledek. Navíc celá sada velkých písmen latinky (bez diakritiky) se nachází před celou sadou odpovídajících malých písmen. To znamená, že libovolné velké písmeno je dle porovnávacího algoritmu _menší_ než libovolné malé písmeno. Budete-li chtít řadit texty podle pravidel nějakého jazyka, je potřeba vytvořit adaptovaný algoritmus nebo použít knihovnu, která rozumí pravidlům daného jazyka.
Doplňte níže několik příkladů, které pomohou vyšetřovat "divné" výsledky porovnání stringů. Případně využijte funkci ord()
, aby bylo zjevně vidět příčiny.
s = [1, 2, "b"]
t = [1, 2, "ba"]
print(s < t)
s = "abcdABCDáÁďĎ"
u = sorted(s)
u
True
['A', 'B', 'C', 'D', 'a', 'b', 'c', 'd', 'Á', 'á', 'Ď', 'ď']
print("zemA" < "zema")
print("země" < "zelí")
print("Země" < "zelí")
print("acd" > 'ac' > "ab" > "a")
True False True True
Příklady¶
- Počet výskytů znaků. Napište funkci, která pro daný string spočte, kolikrát se v něm vyskytuje daný znak. Zatím nepoužívejte žádné metody třídy
str
.
def count_occurrences(text, c):
count = 0
for znak in text:
if znak == c:
count += 1
return count
assert count_occurrences("abrakadabra", "a") == 5
assert count_occurrences("abrakadabra", "c") == 0
assert count_occurrences("", "a") == 0
- Samohlásky. Samohláskami v latinské abecedě jsou písmena A, E, I, O, U a Y. Jiná písmena si považujme za souhlásky. Napište funkci, který spočítá počet samohlásek v daném textu.
def count_vowels(text):
count = 0
vowels = "aeiouyAEIOUY"
for znak in text:
if znak in vowels:
count += 1
return count
assert count_vowels("Eighty percent of success is showing up. Woody Allen") == 17
- Morseovka. Implementujte převod textu na morseovku a zpět pomocí slovníku
to_morse(text)
,from_morse(text)
.
MORSE_CODE = {
"A": ".-", "B": "-...", "C": "-.-.", "D": "-..",
"E": ".", "F": "..-.", "G": "--.", "H": "....",
"I": "..", "J": ".---", "K": "-.-", "L": ".-..",
"M": "--", "N": "-.", "O": "---", "P": ".--.",
"Q": "--.-", "R": ".-.", "S": "...", "T": "-",
"U": "..-", "V": "...-", "W": ".--", "X": "-..-",
"Y": "-.--", "Z": "--..", "1": ".----", "2": "..---",
"3": "...--", "4": "....-", "5": ".....", "6": "-....",
"7": "--...", "8": "---..", "9": "----.", "0": "-----",
}
print(MORSE_CODE["S"])
print(MORSE_CODE.get("S"))
... ...
for key, value in MORSE_CODE.items():
...
def to_morse(text):
vysledek = ""
for pismeno in text:
kod = MORSE_CODE[pismeno]
vysledek = vysledek + kod + " "
return vysledek
print(to_morse("SOS"))
... --- ...
# včetně mezer:
def to_morse(text):
vysledek = ""
for pismeno in text:
if pismeno in MORSE_CODE:
kod = MORSE_CODE.get(pismeno) # MORSE_CODE[pismeno]
vysledek += kod + "/"
else: # mezera
vysledek +="/"
return vysledek
print(to_morse("SOS KOD"))
.../---/...//-.-/---/-../
def from_morse(text):
pismeno = ""
vysledek = ""
for x in text:
if x in ".-":
pismeno += x
else:
for key, value in MORSE_CODE.items():
if value == pismeno:
vysledek += key
#print(pismeno)
pismeno = ""
return vysledek
print(from_morse(to_morse("SOSKOD")))
SOSKOD
# včetně mezer:
def from_morse(text):
pismeno = ""
vysledek = ""
for x in text:
if x in ".-":
pismeno += x
elif x == "/" and pismeno == "": # mezera
vysledek += " "
else:
for key, value in MORSE_CODE.items():
if value == pismeno:
vysledek += key
#print(pismeno)
pismeno = ""
return vysledek
print(from_morse(to_morse("SOS KOD")))
SOS KOD
- Palindrom. Palindrom je posloupnost znaků, která se čte stejně pozpátku jako dopředu. Zjistěte, zda zadaný text je palindrom. Nezapomeňte, že mezera se při čtení neprojevuje. Naprogramujte funkci
is_palindrome(string)
, která vrátíTrue
, pokud zadaný string je palindrom, aFalse
, pokud není. K tomu je možné použit funkci z předchozího úkolu.
def is_palindrome(string):
return string == string[ : :-1]
def is_palindrome(string):
for i in range(len(string)//2+1):
if string[i] != string[-i-1]:
return False
return True
assert is_palindrome("kobylamamalybok")
assert is_palindrome("kobylamamalyboky") == False
- Diagonála. Napište funkci
diagonal(string, lines_count)
, která vypíše zadáný uživatelem řetězecstring
šikmo dolines_count
řádků:
diagonal("abcdefgh", 2) ->
a c e g
b d f h
diagonal("abcdefgh", 3) ->
a d g
b e h
c f
s = "abcdefgh"
print(s[0: : 2])
print(s[1: : 2])
print(s[0: : 3])
print(s[1: : 3])
print(s[2: : 3])
aceg bdfh adg beh cf
def diagonal(string, lines_count):
for i in range(lines_count):
print(" "*i, end = "")
print(*s[i: : lines_count])
diagonal("abcdefgh", 2)
diagonal("abcdefgh", 3)
a c e g b d f h a d g b e h c f
- Heterogram. Heterogram je slovo, v kterém každý znak se vyskytuje nejvýše jednou. Naprogramujte funkci
is_heterogram(slovo)
, která vracíTrue
neboFalse
jako odpověď na otázku, je-li dané slovo heterogram.
def is_heterogram(string):
return len(string) == len(set(string))
assert is_heterogram("abcdeb") == False
assert is_heterogram("abcde")
- Bláznivé křížení zvířat. Máme dva seznamy
s
,t
s názvy zvířat. Napište funkcimerge_animals(s, t)
, která vytvoří nový seznam, který bude obsahovat "zkřížené" názvy zvířat tak, že z prvního názvu vezmeme vždy první polovinu a z druhého druhou polovinu. Např.'hroch' a 'tigr' -> 'hrogr'
.
def merge_animals(s, t):
...
assert merge_animals(["hroch","potkan"],["žirafa","orangutan","lev"]) == ["hrafa", "potgutan"]
- Reverse. Napište funkci
reverse(string)
, která vezme textový řetězec a vrátí ho otočený.
def reverse(string):
return string[::-1]
- Periodické řetězce. Budeme říkat, že řetězec má periodu $k$, pokud ho lze vytvořit zřetězením jednoho nebo více opakování jiného řetězce délky $k$. Např. řetězec
"abcabcabcabc"
má periodu 3, protože je tvořen 4 opakováními řetězce"abc"
. Má také periody 6 (dvě opakování"abcabc"
) a 12 (jedno opakování"abcabcabcabc"
). Napište funkci, která určí nejmenší periodu zadaného řetězce.
def find_period(s):
...
assert find_period("abcabcabcabc")==3
assert find_period("abcabcabcabc ")==10