Přejít k navigační liště

Zdroják » Různé » Optimalizace v Pythonu

Optimalizace v Pythonu

Články Různé

Velmi často se stává, že rychlost výsledné aplikace je nepřímo úměrná rychlosti jejího vývoje – čím rychleji ji vyvineme, tím pomaleji běhá. Pak přichází vhodná chvíle na optimalizace. Pár milisekund ušetřených tu, pár tam, a na výsledku to je náhle znát. V článku si ukážeme, jak optimalizovat aplikace v Pythonu.

Samotný vývoj v Pythonu je velmi rychlý, ale leckdy se to negativně projeví na rychlosti výsledné aplikace. S tím se setkal snad každý, kdo už o Python zaškobrtnul. První verze aplikace byla třeba i rychlá, jenže pak nabobtnaly požadavky, nabobtnal i kód a z rychlíka je líná mrcha. Je načase optimalizovat. Každý optimalizační krok sice může zachránit třeba jen pár milisekund, ale pokud je vhodně smícháte dohromady, tak ušetříte i sekundy, a ty už jsou na pohodlí práce setsakra znát.

Začneme rovnou příkladem, a to z autorovy vlastní praxe. Nedávno dostal autor tohoto textu za úkol optimalizovat refresh webové stránky, protože trval několik sekund! Pokud aplikace měla zrovna dobrou chvilku, tak se stránka obnovila za pět sekund – tak dlouhý čas už dokáže uživatele popudit. Autor si tedy začal hrát, zkoumat různé šikovné techniky, optimalizoval, a výsledkem byl průměrný čas reloadu okolo jedné sekundy. V následujícím textu by se rád s vámi podělil právě o zkušenosti s optimalizacemi.

Začínáme

Začneme čistým pythonským kódem a předvedeme si rychlejší práci s řetězci, seznamy a slovníky.

Pro jistotu upozorním, že ukázky nemusejí být kompletní a zmíněné optimalizace jsou zaměřené na Python 2.x. V Pythonu 3 je spousta věcí změněna; doporučuji se se změnami seznámit dřív, než budete psát rozhořčeně do diskusí. – pozn.aut.

Řetězce

Některé vývojáře v Pythonu možná překvapí následující tvrzení: řetězce jsou neměnné. Ono to při běžné práci s jazykem není na první pohled vidět. O to důležitější je mít to na paměti a podle toho psát kritická místa v aplikaci. Například i pro změnu jednoho znaku na jiný se musí vytvořit nový řetězec, přičemž se většina znaků stejně jen překopíruje. Podívejte se na následující kód:

def foo(filename, spaces=True):
    result = ''
    x = 0
    for line in open(filename):
        result += str(x) + ':    ' + line
        x += 1
    if not spaces:
        result.replace(':    ', ':')
    return result

V této ukázce jsme se dopustili hned několika neoptimálních (čti: nevýkonných) věcí, které budou naši aplikaci zbytečně zdržovat. Bystřejší už jistě vidí, že blok kódu s odstraněním mezer je zbytečný. Proč nejprve vytvořit řetězec a poté ho dodatečně „upravovat“ (ve skutečnosti vytvářet nový podle pravidel), když se mohl vytvořit už správný? Bude vhodné si na začátku funkce připravit proměnnou s obsahem, který bychom si ve výsledku přáli, nějak takto:

def foo(filename, spaces=True):
    result = ''
    x = 0
    separator = ':    ' if spaces else ':'
    for line in open(filename):
        result += str(x) + separator + line
        x += 1
    return result

Lepší, jdeme dál. Další přestupek je ve spojování řetězců. Pro krátké řetězce je někdy skoro výhodnější spojovat je jako v ukázce, ale ani tak to nelze doporučit. Mnohem lepší je využít hezké konstrukce, kterou Python nabízí, a která je pro delší řetězce mnohem výkonnější (platí pro novější verze Pythonu).

 result += „%d%s%s“ % (x, separator, line)

Další vylepšení spočívá v tom, že využijeme zabudovaných funkcí. Pokud to lze, tak zabudovaných funkcí vždy využívejte, protože jsou kompilované a výkonově tedy přebijí jakékoliv jiné řešení napsané v Pythonu. První zabudovaná funkce může být enumarete pro naši proměnnou x, druhá může být map pro nahrazení cyklu a na konec join pro sjednocení pole do jednoho řetězce. Teď jste se možná do funkcí trochu zamotali, takže si ukázku přepíšeme, bude to srozumitelnější:

def foo(filename, spaces=True):
    separator = ':    ' if spaces else ':'
    return ''.join(map(
        lambda param: "%d%s%s" % (param[0], separator, param[1]),
        enumerate(open(filename))
    ))

Není to tak hrozné, jak se zdálo, že? Díky této úpravě se tělo cyklu přesunulo do kompilovaného kódu, a tím se vše výrazně urychlilo. Využívání zabudovaných funkcí se jednoznačně vyplatí!

Seznamy

U řetězců jsme se již zmínili, že je záhodno využívat zabudovaných funkcí. U seznamů si tedy nebudeme ukazovat pomalé řešení, ale rovnou si ukážeme ty rychlejší postupy. Jedno lepší řešení tu už bylo, a to ukázka s funkcí map. Existují další podobné funkce, například filter, ale o těch si přečtete jinde. Místo toho si ukažme další způsob efektivního vytváření seznamů – generator expressions. (Na internetu jsem se dočetl, že by mělo být využívání map rychlejší – ale všechny moje testy ukazují, že tomu není tak a generator expressions je výkonnější než map. – pozn.aut.)

[„%d%s“ % (x, line) for x, line in enumerate(open(filename))]

Schválně si můžete udělat vlastní testy a podělit se o výsledky v diskusi.

Výše zmíněná řešení jsou sice hezká, ale stále je co optimalizovat. Uhodnete, jaký je rozdíl v následujících řádcích?

million = range(1000000)
million = xrange(1000000)

Rozdíl není pouze v písmenku, ale v podání výsledku. První výsledek vygeneruje seznam o milionu prvcích a celý ho přiřadí do proměnné. Druhý případ vrací pouze generátor (negeneruje celý seznam najednou, ale až v cyklu, kde ho použijete, se postupně vrací další a další prvky). Pro zajímavost – range je v tomto případě 2× náročnější než  xrange.

Ano, je pravda, že ne vždy lze generátory využít, ale tam, kde to možné je, jim dejte přednost. Navíc, jak se můžete dočíst v knížce Dive Into Python 3, v Pythonu 3 jsou generátory na každém rohu.

Když už jsme tedy u těch generátorů, tak můžeme místo neefektivního generátoru využít úplného generátoru a přepsat naši ukázku, tedy místo hranatých závorek napsat kulaté:

 („%d%s“ % (x, line) for x, line in enumerate(open(filename)))

Žádná velká změna a ušetří to spoustu milisekund – no není to krásné?

Slovníky

dictionary = {}
for item in list_:
    if not dictionary.has_key(item):
        dictionary[item] = 0
    dictionary[item] += 1

Zde jsme se dopustili dvou prohřešků, které naši aplikaci zaručeně zpomalí. První je tázací metoda nad slovníkem has_key  – metoda je označena za deprecated (zastaralá) a místo ní je lepší použít konstrukci s in, která je efektivnější. Druhý prohřešek je, že se na neexistující klíč ptáme rovnou třikrát! Slovníky mají metodu get, pomocí které můžeme, mimo jiné, předat defaultní hodnotu. Tím v případě, že klíč neexistuje, ušetříme další drahocenný čas.

dictionary = {}
for item in list_:
    dictionary[item] = dictionary.get(item, 0) + 1

Podobný postup lze aplikovat také na objekty, například při zjišťování metody – proč nejprve zkoumat, zda objekt obsahuje danou metodu a pokud ano, tak jej prohledávat znovu a metodu zavolat? Takže místo

if hasattr(object_, method):
    result = getattr(object_, method)()

použijeme třeba

result = getattr(object_, method, lambda: None)()

Na závěr si ke slovníkům ukážeme ještě jednu slovníkovou metodu, která se může hodit:

dictionary.setdefault(key, []).append(element)

K optimalizacím obecně

Že je lepší využívat zabudovaných funkcí, bylo už řečeno několikrát, ale je ještě jedna věc, které byste měli dávat přednost. Jsou to zabudované operátory, například is či in. Pro porovnávání s hodnotou None využívejte jedině is, případně is not; pro hledání položky v seznamu zase výhradně in, případně  not in.

if foo == None: pass # prosim, toto ne
if foo is None: pass # toto ano

if dictionary.has_key(key): pass # prosim, toto take ne
if key in dictionary: pass # toto ano

Obecných věcí bychom si mohli uvádět mnoho, ale záleží hodně na konkrétních okolnostech; navíc nemívají tak velký vliv. Zmiňme alespoň jednu perličku z autorovy praxe:

V zaměstnání máme skvělé logování, díky němuž lze přijít na příčinu téměř jakékoliv chyby. Než se však něco vypíše do logu, tak si výstupní text s různými hláškami upravíme, abychom se v tom vyznali. Samozřejmostí je, že se velmi podrobné logování zapisuje pouze při vývoji, ale naneštěstí se výstupní řetězec skládal pokaždé, i při vypnutém logování. Stalo se to díky tomu, že jsme měli například takovéto volání: log('method call: %s' % logFormat(object_),
priority=42)
 – uvnitř metody log se sice zjistilo, že se zapisovat nic nemá, ale metoda logFormat stejně pracovala, i když „naprázdno“.

Jak je vidět, tak při optimalizaci je důležité aplikaci porozumět – nestačí jen stisknout control+shift+f, hromadně nahradit kusy kódu a říct máme optimalizováno.

Modul psyco

Řekněme, že jste výše zmíněná doporučení zavedli do vaší aplikace, ale stále to není to pravé ořechové. Buď se s tím musíte smířit, přepsat aplikaci do jiného jazyka (nebo třeba jen nějaké klíčové moduly), nebo vyzkoušet modul psyco. psyco je takový „urychlovač“ pythoního kódu. Popravdě vám nemohu prozradit, jak uvnitř funguje, protože jsem se k jeho zkoumání nedostal. Můžete využít oficiální dokumentaci, ale mohu vám říci, co svede. Ještě než začneme s ukázkami, tak je nutno upozornit, že je možné modul využít pouze pro 32-bitové architektury, což je trochu škoda.

import psyco # importovat modul

psyco.full() # kompilovat vsechen kod

psyco.profile(0.2) # kompilovat funkce zabirajici vice nez 20 % casu (cim mene procent, tim vice funkci se bude kompilovat)
psyco.bind(f) # kompilovat pouze danou funkci f

g = psyco.proxy(f)
g(args) # psyco-accelerated volani
f(args) # puvodni, pomale volani

Ukázka je všeříkající. Snad jen – pokud použijeme psyco.full(), tak nemá smysl psát už nic jiného.

Trocha čísel – psyco dokáže urychlit neoptimální konstrukce o 40 až 80 procent! To je sice hezké, ale moc si neporadí s řešením, které je už optimalizované. Když napíšete něco optimálně, tak dokáže psyco výkon takového kódu i řádně zhoršit, proto (pokud se rozhodnete k jeho k používání) rozhodně nepoužívejte „kompilovat vše“, tedy psyco.full(), ale využijte ostatních metod – nejlépe nabindovat jen konkrétní funkce.

Profilování

Prozradíme si pár tipů, co nám může při optimalizaci pomoci. Jako první mohou posloužit obyčejné stopky (pokud nemáte, nevadí – zajděte na Market a nějaké si stáhněte do svého chytrého telefonu). Stopky ale nejsou zrovna přesné a ideální řešení. Stejně dobře může posloužit modul time  – v kódu volejte time.time() na začátku a konci měřené metody a tyto dvě čísla od sebe odečtěte.

import time

t = time.time()
# some code here
print time.time() - t # a mame dobu provadeni v sekundach

Modul time už je o něco lepší, ale existuje ještě daleko lepší řešení – cProfile. Profilování dá podrobnou statistiku o vašem kódu. Můžete dostat například takový výsledek:

         5255 function calls (5154 primitive calls) in 0.057 CPU seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    102/1    0.044    0.000    0.057    0.057 test.py:6(foo)
     5050    0.013    0.000    0.013    0.000 {method 'append' of 'list' objects}
      101    0.001    0.000    0.001    0.000 {range}
        1    0.000    0.000    0.057    0.057 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Je krásně vidět, kolikrát se každá metoda volala. (Připravil jsem si funkci foo, která se volala rekurzivně a naprázdno si v cyklu skládala řetězec.) Je vidět, že se zavolala 102× a celkově to trvalo 44 milisekund. Použil jsem neoptimální sčítání řetězců, range a podobně.

Funkci jsem přepsal na optimální způsob a zkusil zase vyprofilovat:

         205 function calls (104 primitive calls) in 0.002 CPU seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    102/1    0.002    0.000    0.002    0.002 test.py:12(bar)
      101    0.000    0.000    0.000    0.000 test.py:14(<genexpr>)
        1    0.000    0.000    0.002    0.002 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Výsledek je potěšující – padesát milisekund ušetřeno, a kód funguje stejně!

Jak se tedy vlastně profiluje a jak takové funkce s profilováním vypadají:

import cProfile
import pstats

def foo(x):
    if x < 0: return
    l = []
    for a in range(x):
        l.append(str(a) + str(a))
    foo(x-1)

def bar(x):
    if x < 0: return
    g = ("%d%d" % (a, a) for a in xrange(x))
    bar(x-1)

cProfile.run('foo(100)', 'profile-foo')
p = pstats.Stats('profile-foo')
p.sort_stats('time').print_stats(10)

cProfile.run('bar(100)', 'profile-bar')
p = pstats.Stats('profile-bar')
p.sort_stats('time').print_stats(10)

Co dál?

Další studium už ponecháme na laskavém čtenáři. Účelem tohoto článku bylo vzbudit zájem o problematiku optimalizací v Pythonu, a to se snad podařilo.

Dále si můžete pročíst zase trochu jiná doporučení na oficiální wiki Pythonu. Další pěkný text ukazující převod seznamu na řetězec v dokumentaci Pythonu. Určitě bude dobré se podívat na možnosti modulu cProfile také v oficiální dokumentaci. Ale úplně nejlepší je zkusit si vyprofilovat vlastní aplikaci, najít problémová místa a snažit se je optimalizovat – to vám dá víc než tisíc přečtených stránek podobného textu.

Závěrem

Na závěr je třeba důrazně upozornit, že je chybou využívat všechna zmíněná doporučení bezhlavě. Je třeba se dívat na okolní části kódu a podle toho se zachovat. Je také potřeba soustředit se na celý kód, ne jen na jednu část, a vidět věci v celku. Možná vám úprava na jednom místě přidá několik desítek milisekund, ale někde jinde pomůže ušetřit pár stovek(!) milisekund.

Článek si ukončeme malým shrnutím, které není těžké si zapamatovat a které nám pomůže ušetřit spoustu času (tedy toho strojového)

  • Budu využívat zabudované funkce a kompilované knihovničky.
  • Budu upřednostňovat generátory, pokud to bude možné.
  • Budu využívat operátorů in, is a podobně.

Komentáře

Subscribe
Upozornit na
guest
30 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Martin Putniorz

Proč používat Psyco, které je zastaralé, v podstatě neudržované, nejede na x64 a je pomalejší než Pypy? Jinak použití iterátorů je v Pythonu 3 výrazně doporučeno, neboť se chovají líněji než ve verzi 3.

Martin Putniorz

EDIT: samozřejmě se chovají líněji než ve verzi 2.

pipoiuoupokj

Príliš inteligentný redakčný systém vám nahradzuje úvodzovky za okrúhle aj v predformátovanom texte.

pipoiuoupokj

preklep: enumarete

FB

Je příklad optimalizace u řetězce vůbec algoritmicky korektní? Mějme soubor s obsahem „: n: n: „, neoptimalizovaná funkce zavolaná s parametrem spaces=False vrátí řetězece „::::“ a optimalizovaná funkce zavolaná se stejným parametrem vrátí „: :: :: „.

FB

Redakční systém poškodil (přeformátoval) příspěvek. Jde o situaci, kdy v soubor obsahuje podřetězec schodný se separátorem.

beda

Trochu mě zarazilo, že autor vůbec nedokumentoval k jakým úsporám jeho „optimalizacemi“ dojde a protože jsem měl podezření, že jeho verze s „map“ bude spíš pomalejší, schválně jsem si to zkusil.

Verze s map je oproti té předchozí (která slepuje stringy pomocí +, což je mimochodem v novějších verzích pythonu optimalizované pro takovéhle triviální případy a tedy dost rychlé) skoro dvakrát pomalejší (1.38 s vs 0.76 s pro můj test). Hlavní důvod je v tom, že autor porušil svoje vlastní pravidlo o používání vestavěných funkcí a použil vlastní (pythonovou) funkci v podobě lambda funkce. Tím přidal do kódu zbytečné volání funkce, které je v pythonu relativně drahé.
Ono je to totiž tak, že ty „kompilované“ funkce se hodí nejvíce právě do těla cyklu, jedině tak vám může ten map něco ušetřit.

Krab

Přidávám svoje měření. Na mě to moc jako optimalizace nepůsobí.

Měřený kód:

def foo1(f, spaces=True):
    result = ''
    x = 0
    for line in f:
        result += str(x) + ':    ' + line
        x += 1
    if not spaces:
        result.replace(':    ', ':')
    return result

def foo2(f, spaces=True):
    result = ''
    x = 0
    separator = ":    " if spaces else ":"
    for line in f:
        result += str(x) + separator + line
        x += 1
    return result

def foo3(f, spaces=True):
    result = ''
    x = 0
    separator = ":    " if spaces else ":"
    for line in f:
        result += "%d%s%s" % (x, separator, line)
        x += 1
    return result

def foo4(f, spaces=True):
    separator = ":    " if spaces else ":"
    return "".join(map(
                    lambda param: "%d%s%s" % (param[0], separator, param[1]),
                    enumerate(f)
                ))

def foo5(f, spaces=True):
    separator = ":    " if spaces else ":"
    return "".join(("%d%s%s" % (line_no, separator, line) for line_no, line in enumerate(f)))


filename = "566KB_11442_rows.csv"

for x in range(40):
    with open(filename) as f:
        foo1(f)
    with open(filename) as f:
        foo2(f)
    with open(filename) as f:
        foo3(f)
    with open(filename) as f:
        foo4(f)
    with open(filename) as f:
        foo5(f)

Výsledky: (vybrány pouze časy foo?)

$ python -m cProfile -s cumulative rychlost.py
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       40    0.035    0.001    3.510    0.088 rychlost.py:31(foo4)
       40    0.001    0.000    3.272    0.082 rychlost.py:38(foo5)
       40    1.458    0.036    1.458    0.036 rychlost.py:22(foo3)
       40    1.006    0.025    1.006    0.025 rychlost.py:3(foo1)
       40    0.997    0.025    0.997    0.025 rychlost.py:13(foo2)
Mintaka

ncalls tottime percall cumtime percall filename:line­no(function)
37 63.378 1.713 161.906 4.376 <string>:20(foo3)
38 60.914 1.603 160.295 4.218 <string>:1(foo1)
37 60.939 1.647 158.263 4.277 <string>:11(foo2)
37 0.002 0.000 115.776 3.129 <string>:29(foo4)
37 0.002 0.000 114.006 3.081 <string>:36(foo5)

Ani jsem to nenechal doběhnout celé.
Athlon 800MHz

David Grudl

> Druhý prohřešek je, že se na neexistující klíč ptáme rovnou třikrát!

Tohle jsem nepochopil. Proč se ptáme na neexistující klíč třikrát a jak pomohla u neexistujících klíčů optimalizace?

Tomáš

Pokud jsem se správně díval, tak v opraveném kódu se ptá jen dvakrát.

ded kenedy

co se tyce vykonu je navrh pythonu naprosto zoufaly. nejvic dokolen me dostala hlaska v dokumentaci o tom, ze operator „.“ je pomaly… a pokud je potreba ho pouzivat v cyklu, je lepsi si volanou metodu priradit do lokalni promenne.

blizzboz

To v pythone nemáte nič také ako StringBuilder?

ded kenedy

co to s tim ma spolecneho?

noname

Ale je. String ma metodu format().

ded kenedy

pointa je v tom, ze operator tecka se v pythonu nepouziva ke scitani retezcu… takze stringbuilder i format nic neresi :-]]

lyn_x

Je tam snad StringIO, alias memory stream, aspoň se mi to tak při pohledu z rychlíku jeví. A je to asi jedna z nejefektivnějších metod jak v pythonu lepit stringy.

Jerry12

Navrh je mozna mistama zoufalej, ale je potreba srovnavat s konkurenci. Mame vadnouci Perl (6ka bude ready pro moje vnuky, bohuzel), stagnujici PHPcko () a nahypovany Ruby. Ve zmineny konkurenci je Python nejrychlejsi a podle me i nejperspektivnejsi (i kdyz rozstipnuti na 2 a 3 nebyla asi nej volba pod sluncem … ukaze cas a optimalizace ;-))

pjoter

Jo, slava LuaJit :-)

skrat

Zas raz biedne nastudovane tema. Nebudem davat ziadne cisla ale interpolacia retazcov je dost pomala. Ako najrychlejsie riesenie sa mi osvedcil “.join(a,b,c)

Mintaka

Můj nejlepší pomocník pro dělání rychlých programů je můj starý počítač. (Athlon 800MHz s 768MB RAM) Po 12 letech ho stále používám jako svůj hlavní stroj.
Abych jen nečekal nma dončení, nemůžu si dovolit dělat pomalé programy.

Hlavní optimalizace je dobrá analýza problému, který se má řešit.
Znalost kritických míst programu.
Znalost možností, které daný jazyk nabízí, jeho slabin.

http://wiki.python.org/moin/PythonSpeed/PerformanceTips
http://wiki.python.org/moin/TimeComplexity
http://scipy.org/PerformancePython

Neuškodí znalost teorie grafů a výpočetní náročnosti.
http://en.wikipedia.org/wiki/Computational_complexity_theory
http://en.wikipedia.org/wiki/Analysis_of_algorithms

Když není zbytí, jde se o úroveň abstrakce níž.
http://www.root.cz/clanky/moduly-pro-python/
http://www.root.cz/clanky/moduly-pro-python-2/
http://www.root.cz/clanky/vytvarime-v-c-cpp-modul-pro-python/

A níž (ale tam jsem zatím jít nepotřeboval).
http://www.corepy.org/
http://www.grant-olson.net/python/pyasm

JS

Poznamka, ze pro skutecnou rychlost je lepsi jit do C v clanku ponekud chybi. Stejne jako poznamka o algoritmizaci. Obecne, pokud chci neco opravdu rychle, asi si nevyberu Python.

Diky za odkaz na corepy, ne ze bych to zrovna potreboval, ale je to zajimave.

andro

Chybi ti tam Cython po kterem ja saham jako po prvnim. Vlastne ne, kecam – nejdriv optimalizuji pouzitim Numpy, ale to bude tim ze vic pocitam, nez skladam stringy…

Mintaka

Máte pravdu, mohl jsem ho vypíchnout, i když na Cython je odkaz v diskuzi http://www.root.cz/clanky/vytvarime-v-c-cpp-modul-pro-python/nazory/

Tak tedy, http://cython.org/

Pavol

V praxi vacsinou nebyva vykonnostny problem v nejakom generovani zoznamov alebo spajani stringov. Akademicky je to ok, prinos do praxe 0.

Robert

Přihazuji link na docela zajímavé povídání Guida o optimalizaci v Pythonu
http://www.py.cz/OptimalizacniHistorka

anonymous

1. builtiny, bez lambd
2. nestaci-li 1. tak cython
3. nestaci-li 2. (kod pouziva ficury ktere cython dava interpreteru – treba slicovani nekterych typu) tak holt boost-python a c++ (rezie volani je celkem velka ale pointa je zachovat jednoduchost a 1:1 transparenci)

prasit c je az ta posledni moznost protoze cela koncepce pythonu je v tomto dost nestastna.

na nektere specialni ulohy lze pouzit ctypes (treba twisted server co spawnuje thready s sendfile())

pypy je slepa cesta protoze nikdy nemuze konkurovat prvotridnim hand-written JITum jako luajit2 (a sveho casu psyco).

mimi

a proto je pypy v mnoha pripadech i 30x rychlejsi nez CPython ….

proto umoznuje pretvorit python k obrazu svemu viz stackless python …. atd

Mintaka

Také bych se PyPy zastal.

Cite SvenHassel:

PyPy nabízí větší flexibilitu než běžné sémantiky jazyka Python, efektivnější využití paměti, sandboxing poskytuje micro-threads pro masivní paralelismus. PyPy 1.4 je ke stažení pro 32 bitové i 64 bitové systémy (GNU/ Linux, Microsoft Windows a Mac OS X).

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.