Python profesionálně: úvod

Python logo

Programovací jazyk Python přispívá k rychlému vývoji. Dovolí nám nesoustředit se na technické detaily a nechává nám více prostoru na skutečné řešení problému. Python se sice snaží být intuitivní, ale obsahuje věci, které nejsou všední, a příliš se o nich neví. Tímto dnes začínajícím seriálem vám chci Python více přiblížit a odkrýt vám jeho krásu.

Seriál: Programujte v Pythonu profesionálně (5 dílů)

  1. Python profesionálně: úvod 3.4.2012
  2. Python profesionálně: dynamické parametry, generátory, lambda funkce a with 10.4.2012
  3. Python profesionálně: co jazyk nabízí 16.4.2012
  4. Python profesionálně: návrhové vzory 14.5.2012
  5. Python profesionálně: metatřídy 21.5.2012

Úvod k seriálu

Python je velmi zajímavý jazyk. Opravdu je. Na první pohled může vypadat obyčejně, jen s trochu dobrými vychytávkami, avšak po detailnějším zkoumání je tomu jinak. S Pythonem jde vývoj rychle. Nemusíte stále dokola řešit stejné věci. Python pomáhá nové vlastnosti naprogramovat znovupoužitelně.

Co z Pythonu vám chci ukázat? Něco se hodí do příručky pro zelenáče, něco vyžaduje mít jisté znalosti. Zahrnul jsem vše, co mi přišlo zajímavé, a o čem se málo ví. Vznikl tak seriál, kde si snad každý najde to svoje. Dnes začneme ukázkou syntaktických záležitostí a postupně se můžete těšit na různé užitečné tipy, návrhové vzory po pythonovsku a nakonec se těšte na povídání o metatřídách.

Všechny ukázky plně fungují v Pythonu 2.x. Úpravy pro Python 3 jsou minimální, takže se o nich ani nezmiňuji. Pokud však existuje v Pythonu 3 něco navíc nebo se tam vyskytuje nějaká zásadní změna, tak se o tom zmíním.

Začínáme

Pořadí bude, jak už jsem zmínil, od toho nejlehčího, tak schválně, kde odpadnete. :-) Pojďme nyní na první ukázku!

(lambda row: ' '.join(str(cell) if isinstance(cell, (int, long)) else ' ' if cell is None else (cell[::-1].capitalize() if not idx else cell) for idx, cell in enumerate(row) if cell))([6*7, 'is', None, 'rewsna'][::-1])

Uff, z ukázky jsou vidět dvě věci, možná tři: Za prvé, je to nečitelné. Takovým konstrukcím se určitě vyhýbat! Za druhé – v Pythonu je možné prasit, i když nabádá ke štábní kultuře. Proto programujte s otevřenýma očima. A za třetí, jen jsem vás chtěl trochu vylekat. Klid. A teď už se do toho opravdu pusťme.

Ternární operátor

Začneme ternárním operátorem. Já jsem ho dlouho neznal a vadilo mi, že v Pythonu není (ach, jak pošetilé je důvěřovat knížkám). Nejprve jsem to tedy dělal klasicky přes podmínku.

if y:
    x = 'some string'
else:
    x = 'other string'

Zdlouhavé. Když už nic lepšího, alespoň jsem si to zjednodušoval (což lze ve všech jazycích), ač trochu neefektivně. V dnešní době však není podstatné mít efektivitu neustále na paměti.

x = 'other string'
if y:
    x = 'some string'

Když tu jsem jednoho dne prozřel – našel jsem náhražku za ternární operátor!

x = y and 'some string' or 'other string'

Bohužel netrvalo moc dlouho a narazil jsem na problém, kde to nevyhovuje. To když přiřazovaná hodnota při kladném vyhodnocení podmínky je v booleanském smyslu falešná. Ukázka objasní:

x = False and 'a' or 'b' # 'b'
x = True and 'a' or 'b' # 'a'
x = False and '' or 'b' # 'b'
x = True and '' or 'b' # 'b' - ajajaj

Pokud jste tedy nuceni používat Python starší než 2.5 a chcete využívat této konstrukce, dejte si pozor! Případně použijte menší trik (bohužel už to je nepřehledné a raději bych se tomu vyhnul):

x = (True and [''] or ['b'])[0]

Nebudu už ale napínat – jakže vypadá (bezchybný) ternární operátor v Pythonu dostupný od Pythonu 2.5? Takhle:

x = 'some string' if y else 'other string'

Na první pohled se to může zdát trochu nelogické, ale jde jen o zvyk. Zkuste si říct v hlavě klasický ternární operátor ?:. Budete říkat něco jako „když je tohle takhle, tak tamto, jinak tamhleto.“ Zatímco tento lze hezky česky přečíst jako „tamto když je tohle takhle, jinak tamhleto.“ Což mně osobně přijde lepší.

A lze samozřejmě také hezky řetězit…

x = 'variable is %s' % 'one' if y == 1 else 'two' if y == 2 else 'three'

Místo řetězení je ale už lepší použít klasické podmínky. Nebo alespoň přidat závorky, protože to u složitějších konstrukcí může oklamat i takového Tuvoka!

I cyklus má svůj blok else

Řekl bych, že o tom ví velmi málo lidí. A opravdu for cyklus může mít blok else (pravděpodobně záviděl podmínce, holomek). Mockrát se to nepoužije, to je pravda, ale ani tak kvůli nevhodnosti, jako kvůli nezažití. Už jsem si na něj párkrát vzpomněl a byl mi užitečný. Proto se o něm zmiňuji.

Vezměme si příklad: máme seznam automobilů a u jednoho (nebo i u více) potřebujeme odemknout kufr. Pokud ale nenajdeme automobil, u kterého ten kufr potřebujeme odemknout, musíme zavolat nějakou rutinu, třeba informovat uživatele, že pravděpodobně někdo automobil odcizil. Normálně by se napsalo něco jako:

obj = None
for item in listOfCars:
    if item.isCarWhichILookingFor():
        obj = item
        obj.unlockTrunk()
        break

if obj is None:
    print 'Somebody stole car!'

Je to poměrně upovídané řešení. A co je upovídané, u toho je větší šance vzniku chyb. Proto si teď ukážeme, jak funguje blok else u cyklu. Slovy: jestliže cyklus doběhne bez přerušení až nakonec, provede se i blok else, jinak nikoliv. Ukázka:

for x in range(5):
    if x == 1:
        break
else:
    print "else" # Zde se else nevypise.

for x in range(5):
    pass
else:
    print "else" # Zde se else vypise.

S novými vědomostmi zkusíme původní ukázku upravit. Zřejmě už tušíte jak.

for item in listOfCars:
    if item.isCarWhichILookingFor():
        obj.unlockTrunk()
        break
else:
    print 'Somebody stole car!'

Blok else nezáviděl pouze cyklus for, ale také while. Funguje to pochopitelně úplně stejně.

x = 1
while x <= 5:
    try:
        print '%d. attempt to connect to the database.' % x
        connectToDatabase()
    except ConnectTimeoutException:
        x += 1
    else:
        break
else:
    print 'Connection Error!'

Iterace v opačném pořadí

Z předešlého příkladu máme seznam automobilů. S tímto seznamem chceme něco udělat (třeba vypsat do souboru a odeslat kamarádovi), ale v opačném pořadí. Céčkař by pravděpodobně psal šílená céčkovská céčka (nic proti :-).

for i in range(len(listOfCars)-1, -1, -1):
    obj = listOfCars[i]
    # Do something...

Ale my jsme přeci pythonisti! Takže zkusme ukázku upravit:

listOfCars .reverse()
for item in listOfCars:
    # Do something... 

Bohužel to ale zmodifikuje celý seznam. Mohu být zrovna uvnitř nějaké metody, která tento seznam dostala parametrem, a přeci nebudu někomu měnit objekty pod rukama. Pravděpodobně by programátor volající naši metodu nebyl zrovna nadšen. Proto sáhneme po built-in funkci, která iteruje nad seznamem (nebo jakékoliv jiné seznamové instanci) v opačném pořadí.

for item in reversed(listOfCars):
    # Do something...

Teď už to je dobré. Samotný list neměním a nemusím se moc upisovat. Ale stále to lze zkrátit. Pomůžu si takzvanou slice notation ( listOfCars[…]). A že to nejde? Že lze pouze ze seznamu určovat „výřezy“? Kdepak…

for item in listOfCars[::-1]:
    # Do something...

Ona slice notace má totiž ještě třetí parametr, o kterém moc lidí neví, a ten určuje, o kolik se má iterace posouvat na další položku; defaultně o jednu. Tedy [2:3] je to samé, jako [2:3:1], tedy vezme to třetí položku (jedná se o interval zprava otevřený). Pokud napíšu [0:6:2], tak to vycucne první, třetí (skok o dva) a pátou (zase skok o dva) položku; dál už to nepůjde, protože je to omezené zprava.

Slice notace lze dokonce zapsat i built-in metodou slice a tuto instanci (kterou ta metoda vrátí) použít jako klasickou slice notaci.

sl = slice(None, None, -1) if reversedOrder else slice(None)
for item in listOfCars[sl]:
    # Do something...

Tím si můžeme připravit scénář průchodu seznamem v jedné funkci a vykonat iteraci v druhé. Podle mého názoru slice notace už nemůže být lepší!

Hrátky s proměnnými

Čas od času je potřeba vrátit z funkce více hodnot. Řešit tuto potřebu lze různě, od vracení seznamu čí slovníku přes vracení nějaké jiné speciální instance až po rozdělení na více funkcí. Záleží na okolnostech, co je zrovna vhodnější. Například můžeme chtít mít funkci, která z nějakého vstupu rozparsuje klíč a hodnotu:

def parseSameInput(input):
    # Hloupy Honza pro prezentacni ucely.
    return input.split('=')

output = parseSomeInput('key=value')
k = output[0]
v = output[1]

Řešení to je jednoduché, ale stále to není po pythonsku. Kdo by se chtěl takhle upisovat? Existuje snadnější způsob a řekl bych, že už jej znáte, jen si neuvědomujete možnost ho použít kdekoliv.

output = parseSomeInput('hello=world')
k, v = output

Tento způsob se vlastně používá hojně při iteraci slovníku, to staré známé for k, v in d.iteritems(). Ukázka lze však ještě více zjednodušit (všimněte si, že funkci není třeba nijak upravovat).

k, v = parseSomeInput('foo=bar')

A jak to funguje? Nijak zázračně, stačí si uvědomit, že jakmile uděláme čárku, je z toho tuple. A tuple lze rozložit do více proměnných.

a = 3, 5
b = (3, 5)
if a == b:
    print "Promenne jsou stejne!"

Tím lze i snadno prohodit proměnné – pamatujete si na váš první řadící algoritmus bubble sort, kde jste prohazovali proměnné, a museli si nadefinovat pomocnou proměnnou? Zkusme to nyní znovu v Pythonu.

tmp = a
a = b
b = tmp

# vs.

a, b = b, a

Tím ale naše možnosti nekončí, nový Python 3 zvládne ještě jednu specialitu. Ono je to ve skutečnosti logická věc, ale ne každému to může dojít (a vývojářům při vývoji Pythonu 2 ani nedošlo) – tuple (nebo seznam) smí obsahovat více prvků, než do kolika proměnných má být rozložen.

one, two, three, *fourfivesix = range(1, 7)

Jen je potřeba nezapomenout na hvězdičku u poslední proměnné, která má pojmout zbytek.

A když už jsem u toho definování proměnných, tak ještě přidám zmínku na built-in funkce locals a globals. První zmíněná vrací referenci na slovník s lokálními proměnnými a druhá s globálními proměnnými. Když se nad tím člověk zamyslí, tak lze klidně vytvořit následující absurdum.

locals().update(dict((chr(k), v) for k, v in zip(range(97,102), range(1,6))))

Předchozí ukázka v lokálním kontextu vytvoří pět proměnných ae s postupnými hodnotami 1 až 5. Zkuste si to rozluštit sami.

Závěr

Jak je vidět, Python hodně přispívá k rychlému vývoji. Byla by velká škoda ho minimálně nezkusit, protože dnešními tipy to nekončí, dnes to byly spíš jen takové srandičky. Příště budeme pokračovat a konkrétně se podíváme na některé pokročilejší techniky jako generátory, lambda funkce či with konstrukce. Dnes už jen přidám pár odkazů pro další studium:

Michal Hořejšek začal programovat už při studiu průmyslové školy v Jičíně v PHP, dnes pracuje jako Pythonista v Seznam.cz.

Čtení na léto

Jaké knihy z oboru plánujete přečíst během léta? Pochlubte se ostatním ve čtenářské skupině Zdrojak.cz na Goodreads.com.

Komentáře: 85

Přehled komentářů

Jirka Vejražka Hezke, ale PEP8?
Solitary Re: Hezke, ale PEP8?
Jirka V. Re: Hezke, ale PEP8?
Jirka V. Re: Hezke, ale PEP8?
přezdívka Re: Hezke, ale PEP8?
Jirka V. Re: Hezke, ale PEP8?
Inkvizitor Re: Hezke, ale PEP8?
PMD Re: Hezke, ale PEP8?
ByCzech Re: Hezke, ale PEP8?
Jirka Vejražka Re: Hezke, ale PEP8?
ByCzech Re: Hezke, ale PEP8?
Jirka Vejražka Re: Hezke, ale PEP8?
Inkvizitor Re: Hezke, ale PEP8?
petr Re: Hezke, ale PEP8?
Jirka Vejražka Re: Hezke, ale PEP8?
nakrev Re: Hezke, ale PEP8?
Jiří Vraný Re: Hezke, ale PEP8?
ByCzech Re: Hezke, ale PEP8?
Jirka Vraný Re: Hezke, ale PEP8?
Kenny Re: Hezke, ale PEP8?
Jirka Vejražka Re: Hezke, ale PEP8?
Kenny Re: Hezke, ale PEP8?
Piitak Chybka
Michal Hořejšek Re: Chybka
Martin Hassman Re: Chybka
had python verze
Michal Hořejšek Re: python verze
daks Re: python verze
Lada Pěkné čtení
emko +1 ...
zeitung f*ck
Lada Re: f*ck
Ales Zoulek prosim, ucte (se) python s pep8!
Neseznamak Seznam
hejhula Re: Seznam
ornyx Re: prosim, ucte (se) python s pep8!
Neseznamak Re: prosim, ucte (se) python s pep8!
https://profiles.google.com/118074983456106867579 Re: prosim, ucte (se) python s pep8!
Rax Re: prosim, ucte (se) python s pep8!
emko Re: prosim, ucte (se) python s pep8!
Riff Re: prosim, ucte (se) python s pep8!
Inkvizitor Re: prosim, ucte (se) python s pep8!
MilanK Jak zjednodušit tento cyklus
Jirka V. Re: Jak zjednodušit tento cyklus
Havri Re: Jak zjednodušit tento cyklus
Havri Re: Jak zjednodušit tento cyklus
Neseznamak Re: Jak zjednodušit tento cyklus
Havri Re: Jak zjednodušit tento cyklus
MilanK Re: Jak zjednodušit tento cyklus
had uvozovky
Michal Hořejšek Re: uvozovky
had Re: uvozovky
ok3 Re: uvozovky
ByCzech Ternární operátor
Havri Re: Ternární operátor
Michal Hořejšek Re: Ternární operátor
ByCzech Re: Ternární operátor
Raskal Re: Ternární operátor
blizz Re: Ternární operátor
zzzzzzzzz Nemuzu si pomoct...
Neseznamak Re: Nemuzu si pomoct...
Raskal Re: Nemuzu si pomoct...
Radek Miček Re: Nemuzu si pomoct...
Raskal Re: Nemuzu si pomoct...
Radek Miček Re: Nemuzu si pomoct...
Raskal Re: Nemuzu si pomoct...
Honza Kral Python profesionalne
Martin Hassman Re: Python profesionalne
emko Re: Python profesionalne
Michal Hořejšek Re: Python profesionalne
Honza Kral Re: Python profesionalne
Michal Hořejšek Re: Python profesionalne
Amatér Zbytočná kópia
BS-Harou PEP-8
PMD Re: PEP-8
blizz Re: PEP-8
emko Re: PEP-8
blizz Re: PEP-8
emko Re: PEP-8
Čelo počtení
kthxbye plobuh, ternalni operator ?
hejhula "Profesionálně"? Nesouhlasím.
Raskal Re: "Profesionálně"? Nesouhlasím.
Klerik Re: "Profesionálně"? Nesouhlasím.
Klerik Re: "Profesionálně"? Nesouhlasím.
Zdroj: http://www.zdrojak.cz/?p=3622