Guido van Rossum (BDFL), přelom 80. a 90. let, následovník jazyka ABC
16. 10. 2000 – verze 2.0 (komunitní vývoj, full garbage collector, prvotní podpora pro Unicode...)
3. 12. 2008 – verze 3.0, záměrně první zpětně nekompatibilní (skutečná podpora Unicode'u, sjednocení syntaxe a „vnitřností“, vyčištění systémové knihovny...)
Řada 2.x tu bude ještě dlouho*, řada 3.x je ale v mnoha ohledech „hezčí“ jazyk.
* Především kvůli externím knihovnám. Nicméně od ledna 2020 už by neměla býti oficiálně udržována.
Na druhou stranu je třeba přiznat, že trojková řada prošla dosti překotným vývojem a vlastně až verze Python'u 3.2 se dá pokládat za stabilní a rozumně použitelnou.
A podle samotných vývojářů jazyka až verze 3.6 je rychlejší než 2.7…
historie Pythonu, jméno, logo, evoluce 2.x->3.x
http://en.wikipedia.org/wiki/Python_(programming_language)
http://docs.python.org/3.1/license.html
http://www.python.org/psf/
Benevolent Dictator for Life (BDFL)
interpretovaný, dynamicky typovaný vysokoúrovňový jazyk
procedurální, ale i objektový a dokonce i funkcionální
funkce mají výjimečné postavení, jsou zde i uzávěry (closures)
ošetření havarijních situací výjimkami
Dále též:
všechno v Pythonu je objekt, navíc dokonce „first-class object“ (tj. je možné cokoliv předat jako argument do funkce; tedy samozřejmě třeba i jinou funkci!)
"Ahoj, světe!".split()
proměnné se nedeklarují, rovnou se jim přiřazuje hodnota (ale dosud nepoužitou proměnnou nemůžete „zavolat“, dostali byste NameException)
původně existovalo velmi striktní rozlišení mezi příkazy a výrazy (ale zavedení ternárního výrazu to dost nabouralo; škoda že není výrazem aspoň přiřazení…)
Světe div se, od Python'u 3.8 budeme mít k dispozici assignment expressions.
case-sensitive (a má odpovídající konvence na psaní názvů jednotlivých typů objektů) joined_lower - funkce & metody & atributy,
ALL_CAPS - konstanty,
StudlyCaps - třídy
Jinými slovy je Python elegantní jazyk na zápis algoritmů. Ale nečekejte, že by zároveň byl i nějakým přeborníkem ve sprintu, od toho jsou tu jazyky jiné.
~ "All names in Python are case-sensitive: variable names, function names, class names, module names, exception names. If you can get it, set it, call it, construct it, import it, or raise it, it’s case-sensitive."
~ všechno v Pythonu je „first-class object“ - je možné cokoliv předat jako argument do funkce
"This is important, so I’m going to repeat it in case you missed it the first few times: everything in Python is an object. Strings are objects. Lists are objects. Functions are objects. Classes are objects. Class instances are objects. Even modules are objects."
def qs(a):
if a == []:
return []
else:
pivot = a[0]
left = [x for x in a if x < pivot]
right = [x for x in a[1:] if x >= pivot]
return qs(left) + [pivot] + qs(right)
Odsazení jednotlivých částí do bloků je zcela zásadní!
Potřebujete-li vytvořit datovou třídu:
from dataclasses import make_dataclass
Point = make_dataclass('Point', ['x', 'y', 'z'])
p = Point(1.5, 2.5, 3.0)
print(p) # Point(x=1.5, y=2.5, z=3.0)
Pokud musíte zaznamenat i typy nebo dodat k uložení funkcionalitu navíc:
v Pythonu 2.x se dá určit kódování souboru, ale řetězce jsou brány jako sekvence osmibitových znaků
"ASCII string" # Python 3.x
u"Ahoj, světe!" # Python 2.x
až v Pythonu 3.x je konečně všechno nativně UTF-8 (a to dokonce až na úroveň názvů proměnných či funkcí; bohužel s výjimkou výchozího kódování souborových proudů)
"Ahoj, světe!" # textový řetězec v kódování UTF-8
b"bytes" # sekvence bajtů
PS: Od Python'u 3.7 si ale můžete vynutit mód „UTF-8 všude“ vhodným nastavením proměnných prostředí a podobně.
Ale spousta nástrojů s tím pořád ještě nepočítá...
~ Python a kódování modulu/souboru (od Python 3.x je defaultně předpokládáno UTF-8, jiné se musí uvést, stejně jako pro Python 2.x)
http://docs.python.org/dev/3.0/howto/unicode.html
Programovací jazyk má svůj charakter podle toho, jaké obsahuje základní typy a jaké nástroje pro jejich použití nabízí. Postupně se seznámíme s obojím, ale začneme od klíčových slov:
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
Dá se předpokládat, že s výjimkou yield (a možná i lambda, nonlocal nebo as) budete všechno již nějak znát.
abs() divmod() int() print() vars()
all() enumerate() isinstance() property() zip()
any() eval() issubclass() quit()
ascii() exec() iter() range()
bin() exit() len() repr()
bool() filter() list() reversed()
breakpoint() float() locals() round()
bytearray() format() map() set()
bytes() frozenset() max() setattr()
callable() getattr() memoryview() slice()
chr() globals() min() sorted()
classmethod() hasattr() next() staticmethod()
compile() hash() object() str()
complex() help() oct() sum()
delattr() hex() open() super()
dict() id() ord() tuple()
dir() input() pow() type()
Bohužel – jak můžete vidět – „čištění“ Python'u do trojkové verze nebylo tak důkladné, jak mělo být (a to se týká nejen vestavěných funkcí a jejich rozložení na globální úrovni a v modulech).
Kromě toho nabízí Python obsáhlou standardní knihovnu. Najdeme v ní například moduly pro:
práci s reálnými čísly v libovolné přesnosti
generování náhodných čísel
práci s datem
operace nad souborovým systémem (včetně knihovny pro práci s dočasnými soubory)
rozhraní pro databázi SQLite3 (která je součástí distribuce)
práci s komprimovanými a některými textovými a grafickými formáty
obsluhu mailboxu
parsování XML
jednoduchý webserver
GUI založené na Tk (v posledních verzích Python'u už obsahuje i grafická témata)
„odbroukovávání“ kódu
Nejlepší je samozřejmě používat nějakou externí nápovědu nebo alespoň nápovědu, kterou nám poskytuje nějaké vývojové prostředí přímo při psaní kódu. Občas se nám však může stát, že jsme omezeni na příkazovou řádku (např. při vzdáleném přístupu na server), a pak budeme rádi, že Python má k dispozici nápovědu i v takovém případě.
Jádrem této pythonovské nápovědy je funkce help(). Je pochopitelně zamýšlena pro použití v rámci interaktivní konzole a je možno ji použít třemi rozdílnými způsoby:
Zavolání funkce help() bez parametrů přepne interaktivní konzoli – se standardní výzvou >>> – do módu nápovědy, který se hlásí vlastní výzvou help> a reaguje na příkazy nápovědy:
>>> help()
Welcome to Python 3.2! This is the online help utility.
If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://docs.python.org/tutorial/.
Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules. To quit this help utility and
return to the interpreter, just type "quit".
To get a list of available modules, keywords, or topics, type "modules",
"keywords", or "topics". Each module also comes with a one-line summary
of what it does; to list the modules whose summaries contain a given word
such as "spam", type "modules spam".
help>
V tomto módu se můžete přímo ptát na konkrétní prvky jazyka (zkuste napsat např. for), zabudované moduly (zkuste třeba math) a podobně. Zpět do interpretru se vrátíte příkazem quit (bez závorek!).
Na vlastnosti konkrétního objektu se můžete zeptat přímo v interpretru pomocí volání help(OBJEKT):
>>> import math
>>> help(math)
Help on built-in module math:
... # vynechaná spousta textu
Podobně zavoláním help('dotaz') se vám dostane stejného výstupu, jako kdybyste v módu nápovědy (tedy s výzvou help> napsali přímo dotaz)
>>> help('for')
The ``for`` statement
... # vynechaná spousta textu
Dokumentační řetězce jsou obyčejné pythoní řetězce na první řádce příslušného bloku jmenného prostoru*, tedy například hned na začátku těla funkce:
# funkce pro zpracování vstupního čísla
def fnce(číslo):
"""
Funkce fnce() očekává na vstupu právě jedno číslo.
Na svém výstupu vrací toto číslo prohnané modulem zpracovator.
"""
import zpracovator
n = zpracovator.zpracuj()
return n
docstring : jak kód používat
comment : jak a proč daný kód funguje (je napsán)
~ "Docstrings = How to use code | Comments = Why (rationale) & how code works"
Zatímco komentáře jsou ve zdrojovém kódu pouze pro potřeby programátora, dokumentační řetězce jsou plnoprávnou částí kódu. Dokonce tak moc, že nahradí BLOK výkonného kódu a že jsou programově přístupné za běhu programu!
Viz „magický“ objekt __doc__.
PS: Mimochodem – dokumentační řetězce stojí za většinou „magie“ vestavěné nápovědy, tj. za chováním funkce help().
* Kromě prvního řádku těla funkce jsou dalšími možnými umístěními dokumentačních řetězců první řádek těla třídy (tedy hned po class TŘÍDA:) a první řádek modulu (tedy každého skriptu; samozřejmě až po případných řídicích hlavičkách interpretru a kódování). Jako dokumentační řetězec je přitom brán jakýkoli pythoní řetězec, tedy i jednořádkový v jednoduchých uvozovkách či apostrofech, byť samozřejmě víceřádková varianta je výrazně převažující (kvůli nápovědě a především pak kvůli doctestům).
Dokumentační řetězce mají mimochodem ještě jedno velmi šikovné využití – pokud do nich napíšete volání dané funkce jako na příkazové řádce, můžete na uvedený kód (a tudíž i implementaci) později zavolat tzv. dokumentační test:
#!/usr/bin/env python3
def funkce():
'''Tato funkce vždy vrací 666.
>>> funkce()
333
'''
return 666
$ python -m doctest docs.py
**********************************************************************
File "docs.py", line 6, in docs.funkce
Failed example:
funkce()
Expected:
333
Got:
666
**********************************************************************
1 items had failures:
1 of 1 in docs.funkce
***Test Failed*** 1 failures.
Podobnou funkci jako dokumentační řetězce zastává také příkaz pass. Jedná se o jakousi NOP-instrukci – uvedený příkaz je platný, případný jím vytvořený blok je validní částí kódu, jen prostě nic nedělá:
Použití nachází všude tam, kde je očekáván kód, ale buď nemá nic dělat nebo ho teprve časem dopíšeme.
Nejtypičtější použití je asi u definic minimálních tříd nebo přípravu jejich metod:
class MojeTřída:
pass
Světe div se, ale zadat prázdný nic nedělající příkaz se dá ještě jedním a snad ještě výmluvnějším způsobem, totiž pomocí objektu tzv. výpůstky...:
class MojeTřída:
...
Tohle je skutečně validní kód! A každému je jasné, že tu asi chybí implementace třídy ^_~ Jinak se ovšem výpůstky (aneb Ellipsis) používají snad jenom u vynechávaných os v NumPy.
proměnné (mutable)
neproměnné (immutable)
typ
—
int()
číselný
—
float()
číselný
—
complex()
číselný
—
str(), '', ""
sekvenční
bytearray()
bytes(), b'', b""
sekvenční
list(), []
tuple(), (), ,
sekvenční
—
range()
sekvenční
set()
frozenset()
množinový
dict(), {}
—
mapovací
Uvedené funkce slouží zároveň jako konstruktory příslušných prázdných typů.
Číselné typy se berou také jako immutable.
~ Programovací jazyk má svůj charakter podle toho, jaké obsahuje základní typy a jaké nástroje pro jejich použití nabízí.
číselné: + - * ** / // %
logické: andornot
porovnávací: < > == != <= >=
bitové: << >> & | ^ ~
speciální: in , not in ; is , is not
Občas je možné si výraz zkrátit: a = a + 3 je prakticky totéž jako a += 3
Tedy skoro ve všech případech. Interně se na každý z operátorů totiž volá jiná „magická“ metoda, jen je často += namapována na stejnou jako pro +. Ale není to pravda u všech typů!
Pozor na priority (a typy porovnávaných objektů)! Už z důvodu čitelnosti je lepší závorkovat.
# left-shift, right-shift, AND, OR, XOR, invert
>>> a = 5
>>> b = 3
>>> a + b
8
>>> type(5)
<class 'int'>
>>> a += 4
>>> a
9
>>> c = 1.5
>>> c
1.5
>>> type(c+a)
<class 'float'>
>>> 5/2
2.5
>>> 5//2
2
>>> 5**2
25
>>> s = "ahoj"
>>> type(s)
<class 'str'>
>>> s + " světe"
'ahoj světe'
>>> len(s)
4
>>> s[0]
'a'
>>> s[-1]
'j'
Přiřazování je v Python'u celkem tradiční:
>>> a = 'ahoj'
>>> a
'ahoj'
Lze však provést i vícenásobné přiřazení:
>>> a = b = 0
>>> a
0
>>> b
0
Zdánlivě magicky pak vypadají přiřazování u n-tic, ale na ně se podíváme až v příslušné kapitole.
Porovnávání je v Python'u celkem tradiční:
# návratový typ porovnání je Boolean
>>> type(3 == 5)
<class 'bool'>
>>> "bla" == 'bla'
True
# proměnné se porovnávají klasicky
>>> a = b = 0
>>> a == b
True
Kromě tradičního skládání pravdivostních hodnot..
>>> False and False or True
True
>>> not True
False
..však můžeme v Python'u provést i následující:
>>> n = 33
# Složená podmínka:
>>> 1 < n < 100
True
# Její tradiční ekvivalentní (a hůře čitelný) zápis:
>>> (1 < n) and (n < 100)
True
# Zcela v duchu tohoto rozpisu je pak jasný následující výsledek:
>>> 1 < -10 < 100
False
Komentářové řádky s # nejsou součástí výpisu konzole.
# A) neproměnné typy
>>> a = 1
>>> b = 1
>>> a == b
True
>>> a is b
True
# B) proměnné typy
>>> a = [1]
>>> b = [1]
>>> a == b
True
>>> a is b
False
>>> a is not b
True
Komentářové řádky s # nejsou součástí výpisu konzole.
Sekvence jsou vůbec nejdůležitější pythonovský datový typ. Obecně jsou to konečné uspořádané skupiny prvků indexovaných nezápornými čísly, a ač se od sebe v konkrétních vlastnostech často dosti liší, mnoho vlastností a metod základních mají společných:
len(xs)
vrací délku příslušné sekvence xs
xs[i]
vrací prvek sekvence na pozici i (smysl má i výraz typu xs[-2])
xs[i:j]
vrací podsekvenci od indexu i po index j-1 (smysl mají i výrazy typu xs[:3], xs[-2:] apod., někde též xs[i:j:k])
zs = xs + ys
spojování sekvencí
for x in xs:
smyčka přes všechny prvky sekvence
x in xs
zjištění výskytu prvku v sekvenci
xs.index(x[, n])
zjištění prvního umístění prvku v sekvenci (od pozice n)
xs.count(x)
počet výskytů prvku x v sekvenci xs
Pár příkladů na řetězcích (ale úplně stejně to funguje i pro bajtové řetězce a pole, seznamy a n-tice a velmi podobně i pro množiny a slovníky):
>>> s = "Ahoj, světe!"
>>> len(s)
12
>>> s[0]
'A'
>>> s[-2]
'e'
>>> s[2:5]
'oj,'
>>> s[:5]
'Ahoj,'
>>> s[-4:]
'ěte!'
>>> s[2:9:3]
'o ě'
>>> for c in s:
... print(c, end=' ')
...
A h o j , s v ě t e ! >>>
>>> 'A' in s
True
>>> 'a' in s
False
Nejrozsáhlejší datový typ.
http://www.python.org/doc/3.0.1/reference/datamodel.html
Sequences
immutable: Strings, Tuples, Bytes
mutable: Lists, Byte Arrays
Set types
Sets
Frozen sets
Mappings
Dictionaries
Řetězce v Python'u 3.X jsou automaticky unicodové => každý jeden viditelný znak je reprezentován proměnným počtem bajtů.
Narozdíl od Python'u 2.x, kde bylo třeba unicodové řetězce specielně označit u'', jinak byly pokládány za sekvence bajtů v nějakém (předem neurčeném) kódování.
Řetězce patří mezi sekvence, a to neměnitelné (immutable). Vztahují se na ně proto všechny poznámky o sekvencích.
Speciální vlastnosti řetězců:
formátování:
jméno, průměr = 'cosi', 1/3
f"Prvek {jméno} by měl mít průměr {průměr:.3} cm."
jméno, průměr = 'cosi', 1/3
"Prvek {0} by měl mít průměr {1:.3} cm.".format(jméno, průměr)
"Prvek {jmeno} by měl mít průměr {prumer:.3} cm.".format(
jmeno='cosi',
prumer=1/3 ,
)
Původní způsob formátování z Python'u 2.x
'%(lang)s has %(#)03d quote types.' % {
"lang": "Python",
"#": 2,
}
který dává výstup 'Python has 002 quote types.', je v řadě 3.x zatím ještě podporován, i když je označen jako vlastnost/kód určená k budoucímu vyřazení.
dotazy na obsah:
xs.isalpha()
xs.isdigit()
xs.isspace()
xs.islower()
atd.
analýza obsahu:
xs.count("ho")
xs.endswith("!")
xs.find("!")
xs.rfind("!")
atd.
velikost písmen:
xs.swapcase()
xs.upper()
xs.title()
atd.
„prázdné“ znaky &:
xs.strip(['ZNAK[Y]'])
xs.lstrip(['ZNAK[Y]'])
xs.center(DELKA[,'ZNAK'])
xs.rjust(DELKA[,'ZNAK'])
atd.
záměna:
xs.replace('CO', 'ČÍM'[, 'KOLIKRÁT'])
rozdělování (vrací seznam podřetězců):
xlist = xs.split(['ZNAK[Y]'])
xlist = xs.splitlines()
Seznamy jsou také sekvenční (tedy uspořádaný) typ, ale jsou ale měnitelné (mutable), proto také většina jejich metod „operuje“ přímo na příslušném seznamu a málokdy něco vrací.
Prvky seznamu jsou sice typicky jednoho typu, ale to není pravidlo – seznam může obsahovat prakticky cokoliv (samozřejmě i jiné seznamy).
Speciální vlastnosti seznamů:
přidání prvků do seznamu:
xs.append(PRVEK)
xs.insert(POZICE, PRVEK)
xs.extend(SEZNAM)
odebrání prvku ze seznamu:
xs.remove(x)
del xs[i] # obecná obdoba předchozího
x = xs.pop([i])
operace nad celým seznamem:
xs.reverse()
xs.sort()
N-tice jsou neměnitelný (immutable) sekvenční typ. Zjednodušeně se o nich dá uvažovat jako o „zmražených“ seznamech (ostatně zkuste si dir(xs) na nějakou ntici :-), i když se toto pojetí míjí s jejich pravděpodobným původním záměrem.
Použití nachází často např. jako návratové hodnoty z funkcí, protože díky vlastnosti „zabalování a rozbalování“ n-tice umožňují snadno přenést více hodnot v jednom „balíku“ v zaručeném pořadí:
t = ('ahoj', 234, ['a', 3, 5],)
a, b, c = t
Konstruktorem (neprázdné) n-tice je čárka, závorky je tudíž možné vynechat.
Při rozbalování pozor – proměnných na levé straně musí být stejný počet jako prvků n-tice! (Nebo musíte použít např. trik s operátorem *.)
Oblíbený pythoní trik pro výměnu obsahu dvou proměnných využívající rozbalování n-tic pak vypadá následovně:
a, b = b, a
Dejte si ale pozor na porovnávání – vynechané závorky někdy dokážou n-tice úspěšně „zamaskovat“:
Díky existenci datové typu n-tice se můžeme v Python'u pěkně „vyřádit“ při přiřazování (a to mnohem více než u již ukázaného triku a, b = b, a):
Přiřazení více hodnot více proměnným najednou:
>>> v1, v2 = 1.3, "ahoj"
>>> v1
1.3
>>> v2
'ahoj'
>>> del v1
>>> v1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'v1' is not defined
>>> v2
'ahoj'
Uvedený zápis „funguje“ proto, že na obou stranách operátoru přiřazení = se nachází n-tice o dvou členech.
1 a = 1
2 print a
3 del a # we remove the variable a
4 print a # this will cause an error because a is not defined
Přiřazení blíže neurčeného počtu dat do menšího počtu proměnných za pomoci operátoru *:
>>> a, *b, c = 'první', 1, 2, 'ahoj', 'poslední'
>>> a
'první'
>>> b
[1, 2, 'ahoj']
>>> c
'poslední'
„Ohvězdičkovaná“ proměnná pod sebe „schramstne“ všechny přebytečné hodnoty (a ve většině případů se tak stane n-ticí).
Slovník v Python'u je asi nejpodobnější heš-tabulkám z jiných jazyků. Jeho struktura je následující:
Zatímco hodnotou může být cokoliv, klíč musí být hešovatelný datový typ (tj. neměnný, immutable)! Typicky se indexuje řetězcem nebo číslem, ale stejně tak dobře můžete použít například n-tici, pokud se sama skládá z neměnných typů.
Vlastnosti slovníků:
pořadí párů klíč–hodnota je neurčené*, k prvkům slovníku se tak nedá přistupovat podle indexu, zato jsou ale optimalizovány na přístup podle klíče:
>>> d = { 'a': 1, 'b': [1,2,3], 'c': "ahoj", }
>>> d['b']
[1, 2, 3]
* Od Python'u 3.6 si drží pořadí podle zavedení.
přiřazení hodnoty do již existujícího klíče starou hodnotu nahradí novou:
>>> d['a'] = "svet"
>>> d
{'a': 'svet', 'c': 'ahoj', 'b': [1, 2, 3]}
úplně stejným způsobem přidáte do slovníku nový pár klíč–hodnota:
>>> d[123] = (1, 'tuple')
>>> d
{'a': 'svet', 123: (1, 'tuple'), 'c': 'ahoj', 'b': [1, 2, 3]}
a nepotřebného páru se zbavíte pomocí univerzálního del:
>>> del d['b']
>>> d
{'a': 'svet', 123: (1, 'tuple'), 'c': 'ahoj'}
počet párů klíč–hodnota ve slovníku:
len(d)
dotaz na výskyt klíče key ve slovníku d:
key in d
pohled/iterátor přes klíče, resp. hodnoty>
d.keys()
d.values()
pohled/iterátor přes páry (vrací postupně n-tice tvaru (KLÍČ, HODNOTA)):
d.items()
některé další metody slovníků:
d1.update(d2)
d.clear()
d.popitem()
d.pop(KEY)
d.copy()
cyklus for přes slovník v nejpřirozenějším zápise iteruje přes klíče:
for i in d:
print(i)
následující cyklus „rozbalí“ z n-tice zároveň klíč i hodnotu:
for (key, val) in d.items():
print( key, val, sep=': ' )
pokud jsou klíče navzájem porovnatelné, můžeme podle nich páry ve slovníku setřídit:
for key in sorted(d):
print( key, d[key], sep=': ' )
Pozor: Toto setřídění se týká pouze aktuálně prováděného průchodu přes prvky slovníku! Následující průchod slovníkem bez třídění opět vypíše záznamy v (pseudo)náhodném pořadí, resp. v pořadí zadání (od verze 3.6).
Množina je neuspořádaná kolekce jedinečnýchhodnot (a to neměnných, immutable):
odebrání hodnoty:
xs.discard(VALUE)
xs.remove(VALUE) # „KeyError“ pro neexistující hodnotu
x = xs.pop() # „KeyError“ pro prázdnou množinu
smazání množiny:
xs.clear()
(mělká) kopie množiny:
ys = xs.copy()
..máme pro množiny k dispozici i skutečně množinové operace typu sjednocení, průnik, je podmnožinou? a podobně:
vracející novou množinu:
xs.difference(ys) # též xs-ys
xs.symmetric_difference(ys) # též xs^ys
xs.intersection(ys) # též xs&ys
xs.union(ys) # též xs|ys
vracející Boolean:
xs.isdisjoint(ys)
xs.issubset(ys) # též xs<[=]ys
xs.issuperset(ys) # též xs>[=]ys
Přiřazení (či spíše „pojmenovávání“)
Proměnné (mutable) typy jsou v Python'u předávány odkazem, neproměnné (immutable) prozměnu zase hodnotou:
>>> xs = ['a', 'h', 'o', 'j']
>>> ys = xs
>>> ys
['a', 'h', 'o', 'j']
>>> xs.remove('o')
>>> xs
['a', 'h', 'j']
>>> ys
['a', 'h', 'j']
Na začátku si na tom asi párkrát nabijete nos, ale časem na tuhle nakonec celkem logickou podivnost (zvanou, pokud vůbec nějak, předávání objektem) zvyknete.
Základní třídění (řazení) v Python'u je lexikografické, po složkách a stabilní:
Python obecně bude třídit všechny objekty, které interně implementují „magickou“ metodu __lt__(), tedy menší než, pro porovnání svých prvků. Ve výchozím stavu to znamená všechny předdefinované typy.
Generátorová notace (comprehensions) je způsob, jak průchodem po iterovatelném typu snadno a elegantně (typicky v jedné řádce) vyrobit seznam / slovník / množinu. V případě seznamu (notace pro množiny a slovníky je velmi podobná) je obecný zápis:
„[“ Funkce(I) for I in SEKVENCE [if PODMÍNKA] „]“
Pár příkladů pro seznam:
>>> xs = range(1, 9)
>>> xs
range(1, 9)
>>> list(xs)
[1, 2, 3, 4, 5, 6, 7, 8]
>>> [2**n for n in xs]
[2, 4, 8, 16, 32, 64, 128, 256]
>>> [n for n in xs if n % 2 == 0]
[2, 4, 6, 8]
Podmíněný příkaz větvení je v Python'u celkem standardní:
if PODMÍNKA1:
BLOK
elif PODMÍNKA2:
BLOK
else:
BLOK
Přitom:
větve elif a else nejsou povinné
elifů může být libovolně mnoho
Poznámka: V Python'u nejsou konstrukce typu switch / case. Ve většině případů se nahrazují právě elify, v odůvodněných případech přímým voláním na základě slovníkových klíčů.
>>> if 4 == 5:
... print('foo')
... else:
... print('bar')
...
bar
>>> a = "foo"
>>> if a in ['červená', 'zelená', 'modrá']:
... print( a + " je barva." )
... elif a in ['jedna', 'dvě', 'tři']:
... print( a + " je číslo." )
... else:
... print( "Nevím, co je " + a + "." )
...
Nevím, co je foo.
Narozdíl od mnoha jiných věcí bylo volání po podmíněném výrazu vyslyšeno a od Python'u 2.5 je k dispozici v následující pododě:
výstup = VÝRAZ1 if PODMÍNKA else VÝRAZ2
Pořadí zápisu je tedy mezi programovacími jazyky velmi nezvyklé (typicky bývá pomínka první, viz např. PODMÍNKA ? VÝRAZ1 : VÝRAZ2), ale z hlediska Python'u nakonec zcela přirozené.
Python umožňuje smyčku, resp. celý cyklus, předčasně ukončit. Slouží k tomu dva klasické příkazy:
continue
ukončí aktuální smyčku a pokračuje následující
break
ukončí celý aktuální cyklus a přejde na kód následující po něm
Narozdíl od snad naprosté většiny programovacích jazyků se v Python'u cyklí průchodem po prvcích nějakého objektu, ve většině případů sekvence:
for i in SEKVENCE:
BLOK
Navíc narozdíl od většiny ostatních jazyků je proměnná cyklu (zde i) definována na stejné úrovni kódu jako sama smyčka, takže po skončení cyklu bude stále existovat a mít právě tu hodnotu, jíž během provádění cyklu dosáhla (zde poslední prvek procházené sekvence).
Přímou obdobou klasického for-cyklu je pak průchod po prvcích odpovídajícího rozsahu:
for i in range(INTERVAL):
BLOK
Naopak zcela neklasickou je varianta s větví else:
for i in SEKVENCE:
BLOK 1
else:
BLOK 2
Větev s else se provede pouze tehdy, je-li cyklus ukončen normálně, tedy dojde-li k proiterování všech prvků vstupní sekvence. Naopak je-li cyklus v BLOKu 1 ukončen násilně pomocí break, kód v BLOKu 2 nebude vůbec vykonán.
Zde je vstupním objektem, po jehož prvcích se iteruje, seznam tří řetězců:
>>> for word in ["welcome", "to", "python"]:
... print(word)
...
welcome
to
python
Po tomtéž seznamu zde iterujeme za dodatečné pomoci funkce enumerate(), která navíc vrací pořadí prvků v objektu:
>>> for i, word in enumerate(["welcome", "to", "python"]):
... print(i, word)
...
0 welcome
1 to
2 python
Všimněte si použití n-tice na místě řídících proměnných cyklu.
>>> sum = 0
>>> for i in range(10):
... sum += i
...
>>> sum
45
Hodně neklasicky, ale neoptimalizovaně:
>>> for n in range(100):
... for x in range(2,n):
... if n % x == 0:
... break
... else:
... print( n, end=' ')
...
0 1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 >>>
Poznámka: Větev s else u cyklu for se provede pouze tehdy, je-li for-cyklus ukončen normálně, tedy nikoli právě na break.
Cyklus while je na první pohled obdobný jako všude jinde:
while PODMÍNKA:
BLOK
Až do Python'u 3.8 platilo, že v rámci podmínky není možné provést přiřazení do proměnné, ale se zavedením nového speciálního výrazu := už to dnes jde, takže můžete psát:
while (block := f.read(256)) != '':
process(block)
Python byl totiž původně navržen jako jazyk se striktním rozdělením konstrukcí na příkazy a výrazy. A i když to dnes již není na mnoha místech pravda, přiřazení = výrazem není, takže ho nebylo možné použít místo podmínky. Navíc zrovna v tomto místě to bylo zdůvodňováno jen a pouze snadnou záměnou == za =, což nakonec opravdu spravilo právě zavedení jiného způsobu přiřazování (:=). Jinak holt za jazykem nerozlišujícím mezi výrazy a příkazy je se třeba podívat jinam.
Podobně jako u příkazu for-in i pro while existuje velmi neklasická varianta s větví else:
while PODMÍNKA:
BLOK 1
else:
BLOK 2
Větev s else se provede pouze tehdy, je-li smyčka BLOKu 1 ukončena normálně na PODMÍNKu, a je vynechána při jejím násilném ukončení (příkazem break).
V Python'u není cyklus repeat. Nahrazuje se použitím následující konstrukce:
while True:
BLOK
if PODMÍNKA: break
Klasické použití:
>>> i = 0
>>> while i < 5:
... print(i)
... i += 1
...
0
1
2
3
4
Náhrada repeat-until:
>>> while True:
... print( "ahoj" )
... break
...
ahoj
Aneb velmi složitý způsob, jak jednou vypsat řetězec ahoj ^_^
Od Python'u 3.8 je možno použít nový přiřazovací operátor := (zvaný z pochopitelných důvodů walrus :-), který je dohromady s okolostojící proměnnou a výrazem také výrazem (a ne příkazem, jako běžný operátor přiřazení =).
Ač nejznámější použití je pravděpodobně v podmínce cyklu while, v Python'u dává smysl i na mnoha dalších místech, například:
# netřeba volat "len()" dvakrát na to samé
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
# netřeba provádět stejný regexp-match dvakrát
discount = 0.0
if (mo := re.search(r'(\d+)% discount', advertisement)):
discount = float(mo.group(1)) / 100.0
# použití získané hodnoty v podmínce filtru a zároveň i v těle generátorové notace
[clean_name.title() for name in names
if (clean_name := normalize('NFC', name)) in allowed_names]
# použití získané hodnoty v podmínce cyklu a zároveň i v jeho těle
while (block := f.read(256)) != '':
process(block)
Bez invence přímo opsáno z dokumentace.
Pro odchycení a zpracování „havarijních“ situací za běhu programu máme k dispozici výjimky:
try:
BLOK-1
except VÝJIMKA-1:
BLOK-2
except VÝJIMKA-2:
BLOK-3
Kde VÝJIMKA je některá z hromady předdefinovaných nebo naše vyrobená např. jako potomek třídy Exception:
class MyError(Exception):
pass
K bloku try existuje několik dalších užitečných variant:
odchycení výjimky jako pojmenovaného objektu
try:
BLOK-1
except VÝJIMKA as ex:
BLOK-2 # zde je možné použít objekt odchycené výjimky pod jménem 'ex'
odchycení více výjimek v jedné větvi
try:
BLOK-1
except (VÝJIMKA-1, VÝJIMKA-2, ...):
BLOK-2
Kdybyste potřebovali výjimkou přeposlat dál, slouží k tomu příkaz raise. Kromě toho můžete minimálně pro lepší čitelnost (v neoptimalizovaném) kódu použít příkaz assert.
Kromě výše uvedených nabízí výjimky ještě dvě další možnosti:
Větev else:
try:
BLOK-1
except:
BLOK-2
else:
BLOK-3
BLOK-3 bude proveden pouze tehdy, nevyvolal-li kód v BLOKu-1 výjimku (jinak tedy že byl přeskočen BLOK-2).
Větev finally:
try:
BLOK-1
except:
BLOK-2
finally:
BLOK-3
Kód v BLOKu-3 bude proveden vždy, naprosto nezávisle na nastalých výjimkách a jejich odchycení.
Nepřekvapivě se tento způsob zápisu používá nejen pro „skutečné“ výjimky, ale také pro části programu, po nichž je třeba zajistit „vyčišťovací“ operace (uzavírání souborů apod.).
Souhrnně je výjimka poměrně složitá – ale také mocná – konstrukce:
Filozofii za blokem try-finally dovádí k dokonalosti blok with:
with OBJEKT as JMÉNO:
BLOK
Pro objekty, které podporují kontextové zpracování (což jsou z těch důležitějších například soubory), blok with přehlednějším způsobem nahrazuje konstrukci try-except-finally:
Při vstupu do kontextu objektu je zavolána jeho „magická“ metody __enter__() a při jeho opuštění – nezávisle na případných vyvolaných výjimkách – metoda __exit__().
V případě souborů to tak třeba znamená, že se nemusíme starat o jejich uzavření – ať už jejich zpracování proběhne v pořádku nebo během něj dojde k libovolné výjimce, soubor bude korektně uzavřen.
V trojkovém Python'u se kontext with používá nejviditelněji pro práci se soubory:
with open('soubor.txt', mode='r', encoding='utf-8') as soubor:
for i, řádka in enumerate(soubor):
print(i, řádka)
Jinými slovy se tedy při použití with nemusíte „babrat“ se zavíráním souboru (natožpak kdyby během práce s ním došlo k nějaké výjimce).
Python poskytuje velkou podporu pro definici a používání funkcí.
Aby ne, když jsou v něm first class object. (A kromě toho máme také k dispozici hodně ořezané, ale přeci lambda-funkce.)
Novou funkci (v nejjednodušším možném tvaru) zavedeme pomocí klíčového slova def takto:
def jméno_nové_funkce():
BLOK
Odsazení BLOKu jako těla funkce už snad netřeba zdůrazňovat.
Funkce (samozřejmě) mohou mít vstupní parametry a vracet nějaké objekty, jak ukazuje třebas následující příklad:
Přitom:
Vstupních parametrů může být dokonce proměnný počet.
Klasickým příkladem je vestavěná funkce print().
Část (nebo i všechny) parametry mohou mít jméno a výchozí hodnotu.
Pozor pak na předávání proměnných datových typů...
Díky datovému typu n-tice může funkce (zdánlivě) vracet více než jednu hodnotu či objekt.
Což obzvláště v kombinaci s operátory * a ** působí občas jako magie.
U parametrů můžete vynutit, že některé se musí zadávat se jménem (pomocí *) a některé zase pouze pořadím (pomocí /; od verze 3.8). V následujícím kódu parametry a a b můžete zadat pouze pozičně, parametry e a f pouze jménem a u parametrů c a d si můžete vybrat:
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
Pravděpodobně nejpoužívanější funkce Python'u má syntaxi:
>>> s1, s2, s3 = 'ahoj', 'světe', 'jak se máš'
>>> print(s1, s2, s3, sep=' | ', end='\n')
ahoj | světe | jak se máš
Spousta běžných skriptů si přebírá parametry pro svůj běh z příkazové řádky. Ale pro jednodušší případy máme k dispozici jakousi protiváhu funkci print – vestavěnou funkci input(). Ta dokáže přebírat vstup (z klávesnice) do běžícího programu.
Základní použití spočívá v pouhém zavolání funkce input():
>>> answer = input()
Ahoj, světe! # tuhle část musíte napsat (a ukončit ↵) sami
>>> answer
'Ahoj, světe!'
Uvedený kód čeká na zadání vstupního řetězce ukončeného klávesou ↵. Zadaný řetězec (bez odřádkování, to jen potvrdilo vstup) je předán jako návratová hodnota funkce (v našem případě tedy do proměnné answer).
V Python'u 2.x se tímto způsobem chovala globální funkce raw_input().
Metoda input([PROMPT]) má jeden velmi užitečný nepovinný řetězcový parametr, tzv. výzvu (prompt):
>>> answer = input('Zadej text: ')
Zadej text: Ahoj, světe!
>>> answer
'Ahoj, světe!'
PS: Všimněte si, že kurzor pro vstup je umístěn hned za konec řetězce výzvy. Většinou proto asi nebudete chtít zapomenout na ukončovací mezeru.
PS: Chcete-li chování této uživatelské výzvy vylepšit, můžete před jejím použitím naimportovat modul readline (dostupný pouze na platformě Unix!). Vstupní řádka pak „získá“ mimo jiné historii a větší možnosti editace (některé klávesové zkratky apod.).
Již jsme viděli, že Python obsahuje spoustu předdefinovaných funkcí již přímo na globální úrovni (v nulovém jmenném prostoru).
Nejen podle mého by jich tam mělo být daleko méně. Škoda, že to do trojkové verze neořezali pořádně, když už vyráběli zpětnou nekompatibilitu.
Mnohem více funkcí se však schovává na dvou dalších místech:
Známé jsou funkce jako metody konkrétních objektů:
>>> xs = 'ahoj'
>>> xs.upper()
'AHOJ'
Ne už tak známé jsou funkce ve jmenných prostorech konkrétních modulů. V Python'u totiž každý zdrojový soubor může sloužit jako tzv. modul, který je možno pod jeho jménem načíst, naimportovat (za pomoci příkazu import a dalších), čímž se jeho objekty – a to znamená tedy i funkce – stanou přístupnými přes klasickou tečkovou notaci.
Pro příklad se podívejte třeba na modul re v distribuci Python'u.
>>> tup = ('spam', ('eggs', ('lumberjack',
('knights', ))))
>>> stuff = ['a' * 10, tup, ['a' * 10, 'b' * 10],
['c' * 8, 'd' * 8]]
>>> import pprint
>>> pprint.pprint( stuff, width=40 )
['aaaaaaaaaa',
('spam',
('eggs',
('lumberjack', ('knights',)))),
['aaaaaaaaaa', 'bbbbbbbbbb'],
['cccccccc', 'dddddddd']]
Mírně zjednodušený příklad přímo z dokumentace k Python'u.
Klasicky pomocí rekurzivní funkce:
def faktorial(n):
if n == 0:
return 1
else:
return n * faktorial(n-1)
print( faktorial(9) )
Méně přehledně, ale také podstatně méně náročně pomocí obyčejného cyklu:
n = 9
out = 1
while n:
out *= n
n -= 1
print( out )
Podmínka n je sice správná, ale lepší je ji vypsat explicitně jako n >= 0.
Líně pomocí knihovní metody modulu math:
import math
# Return x factorial. Raises ValueError if x is not integral or is negative.
print( math.factorial(9) )
S třídami v Python'u už jsme se několikrát setkali i v tomto úvodu. Stručně řečeno se zavádí pomocí klíčového slova class, a to jako úplně „čisté“ (dědí pouze po obecném objektu Object)..
class MojeTřída:
pass
..nebo jako potomci jiných tříd, jako třeba zde naše vlastní varianta obecné výjimky:
class MojeChyba(Exception):
pass
Třídy, podobně jako funkce, jsou v Python'u velmi mocné, ale trochu jiným způsobem, než je člověk zvyklý z čistě a prvoplánově OOP-jazyků.
Přitom prakticky všechno, na co si můžete v Python'u „šáhnout“, je nějakým způsobem objekt (instance nějaké třídy). Nepřekvapí proto, že Python poskytuje poměrně mocný aparát pro úpravu chování objektů. Většina této funkcionality je schována ve speciálních dvoupodtržítkových atributech.
Python nerozeznává koncepci soukromých, privátních atributů. Ale atributy, jejichž jméno začíná alespoň dvěma podtržítky a končí podtržítkem nejvýše jedním se budou jako privátní tvářit – nejsou totiž na třídě (ani instanci) pod tímto jménem dostupné. (Pokud ovšem víte, že tam jsou, stejně se k nim dokážete dostat.)
Pár ukázek:
__call__()
uvedené třídy (jejich instance) se budou chovat jako funkce (půjde je zavolat)
__enter__()
__exit__()
uvedené třídy bude možné použít uvnitř bloku with
__len__()
__contains__(x)
uvedené třídy se budou chovat jako množiny (tedy bude možné na ně aplikovat množinové operace)
__iter__()
__next__()
__reversed__()
uvedené třídy se budou chovat jako iterátory