6. Znakové reťazce¶
Typ string¶
Čo už vieme o znakových reťazcoch:
reťazec je postupnosť znakov uzavretá v apostrofoch
''
alebo v úvodzovkách""
vieme priradiť reťazec do premennej
zreťaziť (zlepiť) dva reťazce
násobiť (zlepiť viac kópií) reťazca
načítať zo vstupu (pomocou
input()
) a vypisovať (pomocouprint()
)vyrobiť z čísla reťazec (
str()
), z reťazca číslo (int()
,float()
)rozobrať reťazec vo
for
-cykle
Postupne prejdeme tieto možnosti práce s reťazcami a doplníme ich o niektoré novinky.
Keďže znakový reťazec je postupnosť znakov uzavretá v apostrofoch ''
alebo v úvodzovkách ""
, platí:
môže obsahovať ľubovoľné znaky (okrem znaku apostrof
'
v''
reťazci, a znaku úvodzovka"
v úvodzovkovom""
reťazci)musí sa zmestiť do jedného riadka (nesmie prechádzať do druhého riadka)
môže obsahovať špeciálne znaky (zapisujú sa dvomi znakmi, ale pritom v reťazci reprezentujú len jeden), vždy začínajú znakom
\
(opačná lomka):\n
- nový riadok\t
- tabulátor\'
- apostrof\"
- úvodzovka\\
- opačná lomka
Napríklad
>>> 'Monty\nPython'
'Monty\nPython'
>>> print('Monty\nPython')
Monty
Python
>>> print('Monty\\nPython')
Monty\nPython
Viacriadkové reťazce¶
platí:
reťazec, ktorý začína trojicou buď apostrofov
'''
alebo úvodzoviek"""
môže obsahovať aj'
a"
, môže prechádzať cez viac riadkov (automaticky sa sem doplní\n
)musí byť ukončený rovnakou trojicou
'''
alebo"""
>>> macek = '''Išiel Macek do Malacek šošovičku mlácic''' >>> macek 'Išiel Macek\ndo Malacek\nšošovičku mlácic' >>> print(macek) Išiel Macek do Malacek šošovičku mlácic >>> '''tento retazec obsahuje " aj ' a funguje''' 'tento retazec obsahuje " aj \' a funguje' >>> print('''tento retazec obsahuje " aj ' a funguje''') tento retazec obsahuje " aj ' a funguje
Dĺžka reťazca¶
Štandardná funkcia len()
vráti dĺžku reťazca (špeciálne znaky ako '\n'
, '\''
, a pod. reprezentujú len 1 znak):
>>> a = 'Python'
>>> len(a)
6
>>> len('Peter\'s dog')
11
>>> len('\\\\\\')
3
Túto funkciu už vieme naprogramovať aj sami, ale v porovnaní so štandardnou funkciou len()
bude oveľa pomalšia:
def dlzka(retazec):
pocet = 0
for znak in retazec:
pocet += 1
return pocet
>>> dlzka('Python')
6
>>> x = 'x' * 100000000
>>> dlzka(x)
100000000
>>> len(x)
100000000
Operácia in¶
Aby sme zistili, či sa v reťazci nachádza nejaký konkrétny znak, doteraz sme to museli riešiť takto:
def zisti(znak, retazec):
for z in retazec:
if z == znak:
return True
return False
>>> zisti('y', 'Python')
True
>>> zisti('T', 'Python')
False
Pritom existuje binárna operácia in
, ktorá zisťuje, či sa zadaný podreťazec nachádza v nejakom konkrétnom reťazci. Jej tvar je
podretazec in retazec
Najčastejšie sa bude využívať v príkaze if
a v cykle while
, napríklad:
>>> 'nt' in 'Monty Python'
True
>>> 'y P' in 'Monty Python'
True
>>> 'tyPy' in 'Monty Python'
False
>>> 'pyt' in 'Monty Python'
False
Na rozdiel od našej vlastnej funkcie zisti()
, operácia in
funguje nielen pre zisťovanie jedného znaku, ale aj pre ľubovoľne dlhý podreťazec.
Ak niekedy budeme potrebovať negáciu tejto podmienky, môžeme zapísať:
if not 'a' in retazec:
...
if 'a' not in retazec:
...
Pričom sa odporúča druhý spôsob zápisu not in
.
Operácia indexovania [ ]¶
Pomocou tejto operácie vieme pristupovať k jednotlivým znakom postupnosti (znakový reťazec je postupnosť znakov). Jej tvar je
reťazec[číslo]
Celému číslu v hranatých zátvorkách hovoríme index:
znaky v reťazci sú indexované od
0
dolen()-1
, t.j. prvý znak v reťazci má index 0, druhý 1, … posledný má indexlen()-1
výsledkom indexovania je vždy 1-znakový reťazec (čo je nový reťazec s kópiou 1 znaku z pôvodného reťazca) alebo chybová správa, keď indexujeme mimo znaky reťazca
Očíslujme znaky reťazca:
|
|
|
|
|
|
|
|
|
|
|
||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Napríklad do premennej abc
priradíme reťazec 12 znakov a pristupujeme ku niektorým znakom pomocou indexu:
>>> abc = 'Monty Python'
>>> abc[3]
't'
>>> abc[9]
'h'
>>> abc[12]
...
IndexError: string index out of range
>>> abc[len(abc)-1]
'n'
Vidíme, že posledný znak v reťazci má index dĺžka reťazca-1. Ak indexujeme väčším číslom ako 11, vyvolá sa chybová správa IndexError: string index out of range.
Často sa indexuje v cykle, kde premenná cyklu nadobúda správne hodnoty indexov, napríklad:
a = 'Python'
for i in range(len(a)):
print(i, a[i])
0 P
1 y
2 t
3 h
4 o
5 n
Funkcia range(len(a))
zabezpečí, že cyklus prejde postupne pre všetky i
od 0
do len(a)-1
.
Indexovanie so zápornými indexmi¶
Keďže často potrebujeme pristupovať ku znakom na konci reťazca, môžeme to zapisovať pomocou záporných indexov:
abc[-5] == abc[len(abc)-5]
Znaky reťazca sú indexované od -1
do -len()
takto:
|
|
|
|
|
|
|
|
|
|
|
||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
|
-12 |
-11 |
-10 |
-9 |
-8 |
-7 |
-6 |
-5 |
-4 |
-3 |
-2 |
-1 |
Napríklad:
>>> abc = 'Monty Python'
>>> abc[len(abc)-1]
'n'
>>> abc[-1]
'n'
>>> abc[-7]
' '
>>> abc[-13]
...
IndexError: string index out of range
alebo aj for-cyklom:
a = 'Python'
for i in range(1, len(a)+1):
print(-i, a[-i])
-1 n
-2 o
-3 h
-4 t
-5 y
-6 P
alebo for-cyklom so záporným krokom:
a = 'Python'
for i in range(-1, -len(a)-1, -1):
print(i, a[i])
-1 n
-2 o
-3 h
-4 t
-5 y
-6 P
Podreťazce¶
Indexovať môžeme nielen jeden znak, ale aj nejaký podreťazec celého reťazca. Opäť použijeme operátor indexovania, ale index bude obsahovať znak ':'
:
reťazec[prvý : zaposledný]
kde
prvý
je index začiatku podreťazcazaposledný
je index prvku jeden za, t.j. musíme písať index prvku o 1 viactakejto operácii hovoríme rez (alebo po anglicky slice)
ak takto indexujeme mimo reťazec, nenastane chyba, ale prvky mimo sú prázdny reťazec
Ak indexujeme rez od 6. po 11. prvok:
|
|
|
|
|
|
|
|
|
|
|
||
---|---|---|---|---|---|---|---|---|---|---|---|---|
^ |
^ |
|||||||||||
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
prvok s indexom 11 už vo výsledku nebude:
>>> abc = 'Monty Python'
>>> abc[6:11]
'Pytho'
>>> abc[6:12]
'Python'
>>> abc[6:len(abc)]
'Python'
>>> abc[6:12]
'Python'
>>> abc[10:16]
'on'
Podreťazce môžeme vytvárať aj v cykle:
a = 'Python'
for i in range(len(a)):
print(f'{i}:{i+3} {a[i:i+3]}')
0:3 Pyt
1:4 yth
2:5 tho
3:6 hon
4:7 on
5:8 n
alebo
a = 'Python'
for i in range(len(a)):
print(f'{i}:{len(a)} {a[i:len(a)]}')
0:6 Python
1:6 ython
2:6 thon
3:6 hon
4:6 on
5:6 n
Ešte otestujte aj takéto zápisy:
>>> 'Python'[1:-1]
'ytho'
>>> 'Python'[-5:4]
'yth'
>>> 'Python'[-3:3]
''
>>> 'Python'[1:-1][1:-1]
'th'
Dobre sa zamyslite nad každým z týchto reťazcových výrazov.
Predvolená hodnota¶
Ak neuvedieme prvý index v podreťazci, bude to označovať rez od začiatku reťazca. Zápis je takýto:
reťazec[ : zaposledný]
Ak neuvedieme druhý index v podreťazci, označuje to, že chceme rez až do konca reťazca. Teda:
reťazec[prvý : ]
Uvedomte si, že zápisy reťazec[:počet]
a reťazec[-počet:]
budú veľmi často označovať buď prvých alebo posledných počet
znakov reťazca.
Ak neuvedieme ani jeden index v podreťazci, označuje to, že chceme celý reťazec, t.j. vytvorí sa kópia pôvodného reťazca:
reťazec[ : ]
|
|
|
|
|
|
|
|
|
|
|
||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
|
-12 |
-11 |
-10 |
-9 |
-8 |
-7 |
-6 |
-5 |
-4 |
-3 |
-2 |
-1 |
napríklad
>>> abc = 'Monty Python'
>>> abc[6:] # od 6. znaku do konca
'Python'
>>> abc[:5] # od zaciatku po 4. znak = prvých 5 znakov reťazca
'Monty'
>>> abc[-4:] # od 4. od konca az do konca = posledné 4 znaky reťazca
'thon'
>>> abc[16:] # indexujeme mimo retazca
''
Podreťazce s krokom¶
Podobne ako vo funkcii range()
aj pri indexoch podreťazca môžeme určiť aj krok indexov:
reťazec[prvý : zaposledný : krok]
kde krok
určuje o koľko sa bude index v reťazci posúvať od prvý
po posledný
. Napríklad:
>>> abc = 'Monty Python'
>>> abc[2:10:2]
'nyPt'
>>> abc[::3]
'MtPh'
>>> abc[9:-7:-1]
'htyP'
>>> abc[::-1]
'nohtyP ytnoM'
>>> abc[6:] + ' ' + abc[:5]
'Python Monty'
>>> abc[4::-1] + ' ' + abc[:5:-1]
'ytnoM nohtyP'
>>> (abc[6:] + ' ' + abc[:5])[::-1]
'ytnoM nohtyP'
>>> 'kobyla ma maly bok'[::-1]
'kob ylam am alybok'
>>> abc[4:9]
'y Pyt'
>>> abc[4:9][2] # aj podretazce mozeme dalej indexovat
'P'
>>> abc[4:9][2:4]
'Py'
>>> abc[4:9][::-1]
'tyP y'
Reťazce sú v pamäti nemenné (nemeniteľné)¶
Typ str
(znakové reťazce) je nemeniteľný typ (hovoríme aj immutable). To znamená, že hodnota reťazca sa v pamäti nedá zmeniť. Ak budeme potrebovať reťazec, v ktorom je nejaká zmena, budeme musieť skonštruovať nový. Napríklad:
>>> abc[6] = 'K'
TypeError: 'str' object does not support item assignment
Všetky doterajšie manipulácie s reťazcami nemenili reťazec, ale zakaždým vytvárali úplne nový (niekedy to bola len kópia pôvodného), napríklad:
>>> cba = abc[::-1]
>>> abc
'Monty Python'
>>> cba
'nohtyP ytnoM'
Takže, keď chceme v reťazci zmeniť nejaký znak, budeme musieť skonštruovať nový reťazec, napríklad takto:
>>> abc[6] = 'K'
...
TypeError: 'str' object does not support item assignment
>>> novy = abc[:6] + 'K' + abc[7:]
>>> novy
'Monty Kython'
>>> abc
'Monty Python'
Alebo, ak chceme opraviť prvý aj posledný znak:
>>> abc = 'm' + abc[1:-1] + 'N'
>>> abc
'monty PythoN'
Prechádzanie reťazca v cykle¶
Už sme videli, že prvky znakového reťazca môžeme prechádzať for-cyklom, v ktorom indexujeme celý reťazec postupne od 0
do len()-1
:
a = 'Python'
for i in range(len(a)):
print('.' * i, a[i])
P
. y
.. t
... h
.... o
..... n
Tiež vieme, že for-cyklom môžeme prechádzať nielen postupnosť indexov (t.j. range(len(a))
), ale priamo postupnosť znakov, napríklad:
for znak in 'python':
print(znak * 5)
ppppp
yyyyy
ttttt
hhhhh
ooooo
nnnnn
Zrejme reťazec vieme prechádzať aj while-cyklom, napríklad:
a = '.....veľa bodiek'
print(a)
while len(a) != 0 and a[0] == '.':
a = a[1:]
print(a)
.....veľa bodiek
veľa bodiek
Cyklus sa opakoval, kým bol reťazec neprázdny a kým boli na začiatku reťazca znaky bodky '.'
. Vtedy sa v tele cyklu reťazec skracoval o prvý znak. Uvedený while-cyklus môžeme zapísať aj takto:
while a and a[0] == '.':
a = a[1:]
Zamyslite sa, prečo musíme v podmienke kontrolovať, či je reťazec neprázdny. Hoci v tomto prípade by sme to vedeli zapísať aj takto (menej prehľadne):
while a[0:1] == '.':
a = a[1:]
Automatické číslovanie prechodov vo for-cykle¶
Už sme si zvykli, že ak by sme potrebovali v takomto cykle:
for znak in 'Python':
print(znak)
ku každému vypisovanému znaku pridať aj jeho poradové číslo, musíme použiť pomocnú premennú a zyšovať ju v cykle o 1, napríklad takto:
i = 0
for znak in 'Python':
print(i, znak)
i += 1
Keďže teraz už vieme znakové reťazce aj indexovať, vieme to prepísať aj takto:
retazec = 'Python'
for i in range(len(retazec)):
print(i, retazec[i])
Existuje ešte jeden spôsob, ktorý je z týchto „najpythonovejší“ (pythonic). Využijeme ďalšiu štandardnú funkciu enumerate
, pomocou ktorej môžeme for-cyklus zapísať novým štýlom:
for i, znak in enumerate('Python'):
print(i, znak)
Táto funkcia očakáva, že ako parameter dostane nejakú postupnosť (napríklad postupnosť znakov) a jej úlohou bude každý prvok tejto postupnosti očíslovať. Vďaka tomuto for-cyklus dokáže nastavovať naraz dve premenné cyklu: premennú pre očíslovanie (premenná i
) a premennú pre hodnotu (u nás je to premenná znak
). Tento cyklus teda prejde 6-krát, ale zakaždým nastaví naraz dve premenné:
0 P
1 y
2 t
3 h
4 o
5 n
Všimnite si, že číslovanie prechodov cyklu prebieha od 0
a nie od 1
, presne tak, ako je to v Pythone zvykom.
Ukážme ešte jeden príklad s enumerate
. Najprv obyčajný for-cyklus:
for cislo in range(5, 25, 6):
print(cislo)
5
11
17
23
Ak teraz potrebujeme očíslovať každý prechod tohto for-cyklu, môžeme zapísať:
for i, cislo in enumerate(range(5, 25, 6)):
print(i, cislo)
0 5
1 11
2 17
3 23
Pripočítavacia šablóna¶
Pripomeňme si pripočítavaciu šablónu z 2. prednášky, pomocou ktorej sme prešli prvky (znaky) znakového reťazca a poskladali sme z nich dva ďalšie raťazce:
vstup = input('zadaj: ')
pocet = 0
retazec1 = retazec2 = ''
for znak in vstup:
retazec1 = retazec1 + znak
retazec2 = znak + retazec2
pocet = pocet + 1
print('počet znakov reťazca =', pocet)
print('retazec1 =', retazec1)
print('retazec2 =', retazec2)
Táto šablóna znamená to, že pred cyklom inicializujeme premenné, ktoré sa budú v cykle meniť. Najčastejšie budeme tieto premenné v cykle meniť pripočítavaním, alebo odpočítavaním, pridávaním, zreťazovaním a pod. Po skončení cyklu bude v týchto premenných výsledok. V našom príklade bude v premennej retazec1
kópia vstupného reťazca vstup
, v retazec2
bude prevrátená hodnota vstupu a v pocet
počet prechodov cyklu, teda počet znakov v reťazci.
Reťazcové funkcie¶
Už poznáme tieto štandardné funkcie:
len()
- dĺžka reťazcaint()
,float()
- prevod reťazca na celé alebo desatinné číslobool()
- prevod reťazca naTrue
aleboFalse
(ak je prázdny, výsledok budeFalse
)str()
- prevod čísla (aj ľubovoľnej inej hodnoty) na reťazecord()
,chr()
- prevod do a z Unicode
Okrem nich existujú ešte aj tieto tri užitočné štandardné funkcie:
bin()
- prevod celého čísla do reťazca, ktorý reprezentuje toto číslo v dvojkovej sústavehex()
prevod celého čísla do reťazca, ktorý reprezentuje toto číslo v šestnástkovej sústaveoct()
- prevod celého čísla do reťazca, ktorý reprezentuje toto číslo v osmičkovej sústave
Napríklad
>>> bin(123)
'0b1111011'
>>> hex(123)
'0x7b'
>>> oct(123)
'0o173'
Zápisy celého čísla v niektorej z týchto sústav fungujú ako celočíselné konštanty:
>>> 0b1111011
123
>>> 0x7b
123
>>> 0o173
123
Vlastné funkcie¶
Môžeme vytvárať vlastné funkcie, ktoré majú aj reťazcové parametre, resp. môžu vracať reťazcovú návratovú hodnotu. Niekoľko námetov:
funkcia vráti
True
ak je daný znak (jednoznakový reťazec) číslicou:def je_cifra(znak): return '0' <= znak <= '9'
alebo inak
def je_cifra(znak): return znak in '0123456789'
funkcia vráti
True
ak je daný znak (jednoznakový reťazec) malé alebo veľké písmeno (anglickej abecedy)def je_pismeno(znak): return 'a' <= znak <= 'z' or 'A' <= znak <= 'Z'
parametrom funkcie je reťazec s menom a priezviskom (oddelené sú práve jednou medzerou) - funkcia vráti reťazec, v ktorom bude najprv priezvisko a až za tým meno (oddelené medzerou)
def meno(r): ix = 0 while ix < len(r) and r[ix] != ' ': # najde medzeru ix += 1 return r[ix+1:] + ' ' + r[:ix]
funkcia vráti prvé slovo vo vete; predpokladáme, že obsahuje len malé a veľké písmená (využijeme funkciu
je_pismeno
)def slovo(veta): for i in range(len(veta)): if not je_pismeno(veta[i]): return veta[:i] return veta
Reťazcové metódy¶
Je to špeciálny spôsob zápisu volania funkcie (bodková notácia):
reťazec.metóda(parametre)
kde metóda
je meno niektorej z metód, ktoré sú v systéme už definované pre znakové reťazce. My si ukážeme niekoľko užitočných metód, s niektorými ďalšími sa zoznámime neskôr:
reťazec.count(podreťazec)
- zistí počet výskytov podreťazca v reťazcireťazec.find(podreťazec)
- zistí index prvého výskytu podreťazca v reťazcireťazec.lower()
- vráti reťazec, v ktorom prevedie všetky písmená na maléretazec.upper()
- vráti reťazec, v ktorom prevedie všetky písmená na veľkéreťazec.replace(podreťazec1, podreťazec2)
- vráti reťazec, v ktorom nahradí všetky výskytypodreťazec1
iným reťazcompodreťazec2
reťazec.strip()
- vráti reťazec, v ktorom odstráni medzery na začiatku a na konci reťazca (odfiltruje pritom aj iné oddeľovacie znaky ako'\n'
a'\t'
)reťazec.format(hodnoty)
- vráti reťazec, v ktorom nahradí formátovacie prvky'{}'
zadanými hodnotami
Ak chceme o niektorej z metód získať help, môžeme zadať, napríklad:
>>> help(''.find)
Help on built-in function find:
find(...) method of builtins.str instance
S.find(sub[, start[, end]]) -> int
...
Formátovanie reťazca¶
Možnosti formátovania pomocou formátovacích reťazcov f'{x}'
sme už videli predtým. Teraz ukážeme niekoľko užitočných formátovacích prvkov. Volanie má tvar:
f'formátovací reťazec s hodnotami v {}'
Takýto zápis, ale funguje až od verzie Pythonu 3.6 a vyššie. V starších verziách budeme musieť použiť tvar reťazec.format(parametre)
, ale ten tu rozoberať nebudeme, hoci sa veľmi podobá novšiemu zápisu.
Formátovací reťazec obsahuje formátovacie prvky, ktoré sa nachádzajú v {}
zátvorkách: v týchto zátvorkách sa musí náchadzať priamo nejaká hodnota (môže to byť ľubovoľný pythonovský výraz) a za ňou sa môže nachádzať špecifikácia oddelená znakom ':'
. Napríklad:
>>> x = 1237
>>> a = f'vysledok pre x={x} je {x + 8}'
>>> ahoj = 'hello'
>>> b = f'ahoj po anglicky je "{ahoj}"'
Python v tomto prípade najprv vyhľadá všetky výskyty zodpovedajúcich '{}'
a vyhodnotí (vypočíta hodnoty) výrazov, ktoré sú vo vnútri týchto zátvoriek. Tieto hodnoty potom dosadí do reťazca namiesto zápisu {...}
. Zrejme, ak to neboli reťazcové hodnoty (napríklad to boli čísla), tak ich najprv prevedie na reťazce.
V ďalšom príklade sme v {}
použili aj nejaké špecifikácie, t.j. upresnenie formátovania:
>>> r, g, b = 100, 150, 200
>>> farba = f'#{r:02x}{g:02x}{b:02x}'
Špecifikácia formátu¶
V zátvorkách '{}'
sa môžu nachádzať rôzne upresnenia formátovania, ktorými určujeme detaily, ako sa budú vypočítané hodnoty prevádzať na reťazce. Prvé číslo za :
váčšinou označuje šírku (počet znakov), do ktorej sa vloží reťazec a ďalej tam môžu byť znaky na zarovnanie ('<'
, '>'
, '^'
) a znaky na typ hodnoty ('d'
, 'f'
, …). Napríklad:
'{hodnota:10}'
- šírka výpisu 10 znakov'{hodnota:>7}'
- šírka 7, zarovnané vpravo'{hodnota:<5d}'
- šírka 5, zarovnané vľavo, parameter musí byť celé číslo (bude sa vypisovať v 10-ovej sústave)'{hodnota:12.4f}'
- šírka 12, parameter desatinné číslo vypisované na 4 desatinné miesta'{hodnota:06x}'
- šírka 6, zľava doplnená nulami, parameter celé číslo sa vypíše v 16-ovej sústave'{hodnota:^20s}'
- šírka 20, vycentrované, parametrom je reťazec
Zhrňme najpoužívanejšie písmená pri označovaní typu parametra:
d
- celé číslo v desiatkovej sústaveb
- celé číslo v dvojkovej sústavex
- celé číslo v šestnástkovej sústaves
- znakový reťazecf
- desatinné číslo (možno špecifikovať počet desatinných miest, inak default 6)g
- desatinné číslo vo všeobecnom formáte
Dokumentačný reťazec pri definovaní funkcie¶
Ak funkcia vo svojom tele hneď ako prvý riadok obsahuje znakový reťazec (zvykne byť viacriadkový s '''
), tento sa stáva, tzv. dokumentačným reťazcom (docstring). Pri vykonávaní tela funkcie sa takéto reťazce ignorujú (preskakujú). Tento reťazec (docstring) sa, ale môže neskôr vypísať, napríklad štandardnou funkciou help()
.
Zadefinujme reťazcovú funkciu a hneď do nej dopíšeme aj niektoré základné informácie:
def pocet_vyskytov(podretazec, retazec):
'''funkcia vráti počet výskytov podreťazca v reťazci
prvý parameter podretazec - ľubovoľný neprázdny reťazec, o ktorom sa
bude zisťovať počet výskytov
druhý parameter retazec - reťazec, v ktorom sa hľadajú výskyty
ak je prvý parameter podretazec prázdny reťazec, funkcia vráti len(retazec)
'''
pocet = 0
for ix in range(len(retazec)):
if retazec[ix:ix+len(podretazec)] == podretazec:
pocet += 1
return pocet
Takto definovaná funkcia funguje rovnako, ako keby žiaden dokumentačný reťazec neobsahovala, ale teraz bude fungovať aj:
>>> help(pocet_vyskytov)
Help on function pocet_vyskytov in module __main__:
pocet_vyskytov(podretazec, retazec)
funkcia vráti počet výskytov podreťazca v reťazci
prvý parameter podretazec - ľubovoľný neprázdny reťazec, o ktorom sa
bude zisťovať počet výskytov
druhý parameter retazec - reťazec, v ktorom sa hľadajú výskyty
ak je prvý parameter podretazec prázdny reťazec, funkcia vráti len(retazec)
Tu môžeme vidieť užitočnú vlastnosť Pythonu: programátor, ktorý vytvára nejaké nové funkcie, môže hneď vytvárať aj malú dokumentáciu o jej používaní pre ďalších programátorov. Asi ľahko uhádneme, ako funguje napríklad aj toto:
>>> help(hex)
Help on built-in function hex in module builtins:
hex(number, /)
Return the hexadecimal representation of an integer.
>>> hex(12648430)
'0xc0ffee'
Pri takomto spôsobe samodokumentácie funkcií si treba uvedomiť, že Python v tele funkcie ignoruje nielen všetky reťazce, ale aj iné konštanty:
ak napríklad zavoláme funkciu, ktorá vracia nejakú hodnotu a túto hodnotu ďalej nespracujeme (napríklad priradením do premennej, použitím ako parametra inej funkcie, …), vyhodnocovanie funkcie takúto návratovú hodnotu ignoruje
ak si uvedomíme, že meno funkcie bez okrúhlych zátvoriek nespôsobí volanie tejto funkcie, ale len hodnotu referencie na funkciu, tak aj takýto zápis sa ignoruje
Napríklad všetky tieto zápisy sa v tele funkcie (alebo aj v programovom režime mimo funkcie) ignorujú:
s.replace('a', 'b')
print
g.pack
pocet + 1
i == i + 1
math.sin(uhol)
Python pri nich nehlási ani žiadnu chybu.
Príklad s kreslením a reťazcami¶
Navrhnime malú aplikáciu, v ktorej budeme pohybovať mysleným perom. Toto pero sa bude hýbať v jednom zo štyroch smerov: 's'
pre sever, 'v'
pre východ, 'j'
pre juh, 'z'
pre západ. Dĺžka kroku pera nech je nejaká malá konštanta, napríklad 10
:
import tkinter
def kresli(retazec):
x, y = 100, 100
for znak in retazec:
x1, y1 = x, y
if znak == 's':
y1 -= 10
elif znak == 'v':
x1 += 10
elif znak == 'j':
y1 += 10
elif znak == 'z':
x1 -= 10
else:
print('nerozumiem "' + znak + '"')
return
canvas.create_line(x, y, x1, y1)
x, y = x1, y1
canvas = tkinter.Canvas()
canvas.pack()
kresli('ssvvjjzz')
tkinter.mainloop()
Po spustení dostaneme:

Zrejme rôzne reťazce znakov, ktoré obsahujú len naše štyri písmená pre smery pohybu, budú kresliť rôzne útvary. Napríklad:
kresli('vvvvvvvjjjjjjjzzzzzzzsssssss')
nakreslí trochu väčší štvorec. Toto vieme zapísať napríklad aj takto:
kresli('v'*7 + 'j'*7 + 'z'*7 + 's'*7)
Alebo
def stvorec(n):
return 'v'*n + 'j'*n + 'z'*n + 's'*n
kresli(stvorec(7))
Na cvičeniach budeme rôzne vylepšovať túto ideu funkcie kresli()
.
Cvičenia¶
Ručne bez počítača zisti, čo sa vypíše:
>>> x, y = 'Bratislava', 'Košice' >>> y[1] + x[4] + y[3] + x[-4] + y[-5] ... >>> x[5:8] + 3 * x[3] + y[2:] ... >>> y[:2] + x[-2:] ... >>> x[1::2] + y[2::2] + x[2::3] ... >>> x.replace('a', 'e') + y.replace('ic', 'm') ... >>> (y + x).replace('i', '').replace('a', 'xa') ...
Potom to skontroluj pomocou Pythonu.
Napíš funkciu
sucet(retazec)
, ktorá dostáva znakový reťazec s dvomi celými číslami oddelenými znakom'+'
. Funkcia vráti (nič nevypisuje) celé číslo, ktoré je súčtom dvoch čísel v reťazci. Použi rezy, metóduretazec.find('+')
a funkciuint()
. Napríklad:>>> x = sucet('12+9') >>> x 21 >>> sucet('987654321+99999') 987754320
Zovšeobecni funkciu
sucet(retazec)
z predchádzajúcej úlohy: vstupný reťazec obsahuje aspoň jedno číslo a keď ich je viac, sú oddelené znakom'+'
. Funkcia vypočíta súčet. Napríklad:>>> x = sucet('12+9') >>> x 21 >>> sucet('1+2+3+4') 10 >>> sucet('1234') 1234
Napíš funkciu
postupnost(start, koniec)
, ktorá vytvorí znakový reťazec z postupnosti číselrange(start, koniec)
. Čísla v tejto postupnosti budú oddelené znakom medzera' '
. Napríklad:>>> p = postupnost(5, 13) >>> p '5 6 7 8 9 10 11 12'
Do funkcie pridaj ešte jeden parameter
postupnost(start, koniec, krok=1)
tak, aby fungovalo napríklad:>>> p = postupnost(13, 5, -2) >>> p '13 11 9 7'
Napíš funkciu
rozsekaj(text, sirka)
, ktorá vypíše zadaný text do viacerých riadkov, pričom každý (možno okrem posledného) má presnesirka
znakov. Napríklad:>>> ret = rozsekaj('Anicka dusicka, kde si bola', 10) >>> ret 'Anicka dus\nicka, kde \nsi bola' >>> print(ret) Anicka dus icka, kde si bola
Napíš funkciu
stvorec(n, znak)
, ktorá vráti jeden dlhý znakový reťazec. Znakový reťazec by po vypísaní pomocouprint
vytvoril obvod štvorca z daného znaku. Môžeš predpokladať, žen
nebude menšie ako 2. Napríklad:>>> r = stvorec(5, '#') >>> r '#####\n# #\n# #\n# #\n#####' >>> print(r) ##### # # # # # # #####
Pokús sa to vyriešiť tak, že telo funkcie bude obsahovať jediný riadok
return
:def stvorec(n, znak): return ...
Napíš funkciu
vyhod_duplikaty(retazec)
, ktorá z daného reťazca vyhodí všetky za sebou idúce opakujúce sa znaky (nechá len jeden z nich). Napríklad:>>> x = vyhod_duplikaty('Braatisssllavaaaaa') >>> x 'Bratislava'
Napíš funkciu
ozatvorkuj(retazec, podretazec)
, ktorá v zadanom reťazciretazec
všetky výskyty daného podreťazca ozátvorkuje. Napríklad:>>> b = ozatvorkuj('Bratislava', 'a') >>> b 'Br(a)tisl(a)v(a)' >>> ozatvorkuj('prospešné programovanie v prologu', 'pro') '(pro)spešné (pro)gramovanie v (pro)logu'
Znakový reťazec vieme prevrátiť pomocou zápisu
retazec[::-1]
. Napíš funkciuprevrat(retazec)
, ktorá len pomocou cyklu a zreťazovania prevráti zadaný reťazec. Napríklad:>>> x = prevrat('tseb eht si nohtyP') >>> x 'Python is the best'
Napíš funkcie
male(retazec, i)
avelke(retazec, i)
, ktoréi
-te písmeno v reťazci prerobia na malé (resp. veľké). Napríklad:>>> r = male('PYTHON', 3) >>> r 'PYThON' >>> r = velke('python', 5) >>> r 'pythoN'
Napíš funkciu
riadky(retazec)
, ktorá vypíše daný viacriadkový reťazec, ale pritom každý riadok očísluje číslami od 1 do počet riadkov. Napríklad:>>> riadky('prvy riadok\n\ntreti je posledny') 1. prvy riadok 2. 3. treti je posledny >>> riadky('len \\n jeden riadok') 1. len \n jeden riadok
Napíš funkciu
posun_znak(znak, posun)
, ktorá posunie daný znak v abecede op
znakov vpravo (resp. vľavo, ak je záporné). Na konci abecedy sa pokračuje od začiatku. Funkcia posúva len písmená malej abecedy, ostatné znaky nemení. Napríklad:>>> posun_znak('c', 4) 'g' >>> posun_znak('g', -4) 'c' >>> posun_znak('x', 10) 'h' >>> posun_znak('A', 10) 'A'
Napíš funkciu
zakoduj(text, posun)
, ktorá posunie v abecede všetky znaky (pomocou funkcieposun_znak
). Napríklad:>>> x = zakoduj('pyThon', 10) >>> x 'ziTryx' >>> zakoduj(x, -10) 'pyThon' >>> zakoduj(x, 16) 'pyThon'
Napíš funkciu
je_palindrom(reťazec)
, ktorá zistí (vrátiTrue
aleboFalse
), či je zadaný reťazec palindróm. Funkcia ignoruje medzery a nerozlišuje medzi malými a veľkými písmenami. Napríklad:>>> je_palindrom('Python') False >>> je_palindrom('tahat') True >>> je_palindrom('Jelenovi Pivo Nelej') True
Metóda
'reťazec'.count(podreťazec)
zisťuje počet výskytov podreťazca v reťazci. Napíš funkciupocet(retazec, podretazec)
, ktorá robí to isté, ale bez použitia tejto metódy. Napríklad:>>> pocet('mama ma emu a ema ma mamu', 'ma ') 4 >>> pocet('mama ma emu a ema ma mamu', 'am') 2
Napíš funkciu
usporiadaj(h1, h2, h3)
, ktorá z troch zadaných hodnôt (všetky tri sú rovnakého typu, napríklad reťazce) vytvorí reťazec (vráti ho ako výsledok funkcie) zlepením týchto troch hodnôt v utriedenom poradí: najprv najmenšia (napríklad reťazec prvý v abecede), potom väčšia a na koniec najväčšia. Medzi zlepené reťazce vloží medzeru. Napríklad:>>> x = usporiadaj('python', 'pytliak', 'pytagoras') >>> x 'pytagoras python pytliak' >>> usporiadaj(345, 123, 234) '123 234 345'
Napíš funkciu
nazov(n)
, pomocou ktorej sa bude dať vygenerovať názov hudobnej skupiny. Chceme, aby toto meno začínalo a končilo rovnakou samohláskou a medzi týmito samohláskami by sa malán
krát objaviť nejaká spoluhláska. Prvé písmeno mena by malo byť veľké ostatné malé. Zrejme využiješ ideu z 2. prednášky, pomocou ktorej sa náhodne generovali písmená. Môžeš dostať napríklad:for i in range(5): print(nazov(2)) print(nazov(3))
Uxxu Uxxxu Ippi Idddi Atta Ottto Yggy Odddo Aqqa Ibbbi
Unicode
0x2654
a ďalších päť za ním sú obrázky šachových figúrok. Napíš program, ktorý do grafickej plochy nakresli vedľa seba všetkých 6 figúrok náhodnými farbami väčším fontom (napríklad'arial 50'
). Môžeš dostať takýto obrázok:
Napíš funkciu
stvorce(vel, retazec)
, ktorá dostáva dva parametre: veľkosť štvorca a znakový reťazec s menami farieb. Funkcia nakreslí rad farebných štvorcov veľkostivel
, ktoré budú zafarbené farbami z reťazca. Zrejme štvorcov bude toľko, koľko farieb je v reťazci. Pre takéto volanie:stvorce(40, 'red blue purple red gold')
by si mohol dostať takýto obrázok:
Úloha bude podobná predchádzajúcej: napíš funkciu
stvorce(retazec)
, ktorá dostáva v reťazci informáciu o veľkosti a farbe štvorcov. Funkcia bude tieto štvorce kresliť vedľa seba, ale len dovtedy, kým by nasledovný nevypadol z grafickej plochy (tento reťazec sa stále opakuje od začiatku). Do premennejsirka
nastav nejakú šírku grafickej plochy a zavolaj funkciu, napríklad takto:sirka = 450 canvas = tkinter.Canvas(width=sirka) canvas.pack() stvorce('40 red 20 blue 60 purple 40 red 30 gold')
Mohol by si dostať takýto obrázok:
Na konci prednášky je funkcia
kresli(retazec)
, pomocou ktorej môžeme vytvárať nejakú kresbu zakódovanú písmenami'svjz'
. Nakresli pomocou tejto funkcie takýto obrázok:
Dopíš do tejto funkcie spracovanie týchto ďalších znakov:
'h'
- kresliace pero sa bude odteraz pohybovať bez kreslenia (pero hore)'d'
- kresliace pero bude odteraz pri pohybe kresliť (pero dole)číslice od
'1'
do'9'
- nasledovný príkaz (jeden z'svjz'
) sa vykoná príslušný počet krátnapríklad:
>>> kresli('4v4j4z4sh5vd'*5)
nakreslí vedľa seba 5 štvorcov:
3. Týždenný projekt¶
Napíš pythonovský skript, ktorý bude definovať tieto 4 funkcie:
funkcia
pocet_dni_v_mesiaci(mesiac, priestupny=False)
- vráti číslo od28
do31
, pričom parameter mesiac je znakový reťazec, v ktorom sú dôležité len prvé tri znaky, tie sú'jan'
,'feb'
,'mar'
,'apr'
,'maj'
,'jun'
,'jul'
,'aug'
,'sep'
,'okt'
,'nov'
,'dec'
,zrejme
pocet_dni_v_mesiaci('feb', True)
vráti29
funkcia
pocet_dni_medzi(datum1, datum2)
- pre dva dátumy vypočíta počet dní, ktoré sú medzi nimioba dátumy sú zadané ako znakové reťazce v tvare
'mesiac.rok'
, pričom pre mesiac sú dôležité len prvé tri znakyprvý dátum je vlastne
1.mesiac1.rok1
, druhý dátum je1.mesiac2.rok2
môžeš predpokladať, že prvý dátum je pred alebo rovný druhému dátumu; keď sa oba dátumy rovnajú, funkcia vráti
0
oba roky budú v intervale
<1901, 2099>
napríklad:
>>> print(pocet_dni_medzi('sep.2022', 'okt.2022')) # medzi 1.9.2022 a 1.10.2022 30 >>> print(pocet_dni_medzi('okt.2023', 'okt.2024')) # medzi 1.10.2023 a 1.10.2024 366 >>> print(pocet_dni_medzi('januar.1999', 'oktober.2022')) # medzi 1.1.1999 a 1.10.2022 8674
zrejme využiješ funkciu
pocet_dni_v_mesiaci()
a to, že priestupný rok (rok%4==0
) má366
dní a nepriestupný365
funkcia
den_v_tyzdni(datum)
, kdedatum
je znakový reťazec vo formáte'den.mesiac.rok'
mesiac v tomto dátume je znakový reťazec, v ktorom sú dôležité len prvé tri znaky
funkcia vráti deň v týždni ako trojznakový reťazec, jeden z
'pon'
,'uto'
,'str'
,'stv'
,'pia'
,'sob'
,'ned'
môžeš to počítať tak, že najprv zistíš počet dní, ktoré uplynuli od dátumu 1.január.1901 a keďže vieme, že vtedy bol utorok, ľahko z toho vypočítaš deň v týždni (bude to nejaký zvyšok po delení
7
)môžeš predpokladať, že dátum bude zadaný korektne a bude z intervalu
<1.jan.1901, 31.dec.2099>
napríklad:
>>> den_v_tyzdni('6.oktober.2022') 'stv' >>> den_v_tyzdni('1.jan.1901') 'uto' >>> den_v_tyzdni('23.jun.1912') 'ned'
Vedel by si zistiť, čím je dátum 23. júna 1912 zaujímavý?
kalendar(datum)
- vypíše (pomocouprint()
) kalendár pre jeden mesiac v takomto tvaredátum je zadaný ako znakový reťazec v tvare
'mesiac.rok'
, pričom pre mesiac sú dôležité len prvé tri znakynapríklad:
>>> kalendar('oktober.2022') pon uto str stv pia sob ned 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 >>> kalendar('maj.1945') pon uto str stv pia sob ned 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
prvý riadok obsahuje mená dní v týždni presne v tomto tvare, ďalej nasledujú dni v mesiaci, ktoré sú naformátované presne do zodpovedajúcich stĺpcov
treba si dať pozor na medzery
Na otestovanie správnosti svojich funkcií môžeš využiť, napríklad tento štandardný modul (v tvojom riešení ho zrejme použiť nesmieš):
>>> import calendar
>>> calendar.weekday(2022, 10, 6) # 6.oktober 2022, vráti: 0=pondelok, 1=utorok, ... 6=nedela
3
>>> calendar.day_name[calendar.weekday(2022, 10, 6)]
'Thursday'
>>> calendar.prmonth(2022, 10, 3) # oktober 2022, čísla na šírku 3 znaky
October 2022
Mon Tue Wed Thu Fri Sat Sun
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Tvoj odovzdaný program s menom riesenie.py
musí začínať tromi riadkami komentárov (zmeň na svoje meno a dátum odovzdania):
# 3. zadanie: kalendár
# autor: Janko Hraško
# datum: 10.10.2022
V programe používaj len konštrukcie jazyka Python, ktoré sme sa učili na prvých 6 prednáškach. Nepoužívaj príkaz import
.
Súbor riesenie.py
odovzdaj na úlohový server https://list.fmph.uniba.sk/. Môžeš zaň získať 5 bodov.