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

Zdroják » Různé » Python profesionálně: metatřídy

Python profesionálně: metatřídy

Články Různé

V předchozích dílech tohoto seriálu jsme si řekli spoustu zajímavých tipů, jak vyvíjet v Pythonu lépe a rychleji. Od syntaktických tipů přes různé tipy na vestavěné funkce, moduly atp. až po zajímavé řešení některých návrhových vzorů. Zbývá už jen poslední a pravděpodobně nejnáročnější povídání – o metatřídách. Co to vlastně je, jak se tvoří a kde se dají využít.

Hned na úvod: pokud se momentálně moříte s nějakým návrhem aplikace, metatřída vaše spása nebude. Většinou metatřída není řešením. Minimálně pro běžné aplikace, metatřídy se hodí spíše pro nějaké knihovničky, vychytávky a tak podobně. Ale doporučuji si o nich něco málo zjistit, i když to využijete málo či vůbec, abyste pochopili samotný Python nebo minimálně cizí kód, kde jsou metatřídy použité.

Jednu metatřídu jsme už v seriálu použili, bylo to pro snadnější zápis návrhového vzoru singleton. Připomeňme si kód, který se budeme snažit dnes pochopit:

class SingletonMeta(type):
    def __new__(cls, classname, bases, classdict):
        classdict.setdefault('__slots__', ())
        newcls = type.__new__(cls, classname, bases, classdict)
        return newcls()

class singleton(object):
    __metaclass__ = SingletonMeta

Trocha teorie

Konstruktor

Než se ale dáme do zjišťování, co se v ukázce děje, musíme si chvilku povídat. Začněme metodou __init__  – kdo ji nazývá konstruktor? Prosím, nesmějme se těm, kdo se právě hlásí. Všichni jsme ji tak dříve nazývali. Metoda __init__ není konstruktor. Je to z velmi jednoduchého důvodu – __init__ přece žádnou instanci nevytváří. Tu už dostává v parametru jako všechny ostatní instanční metody. Důkazem nechť je dokumentace: „Called when the instance is created.“

Co by to ale bylo za programovací jazyk, kdyby zde konstruktor nebyl. Ba je, a je skryt v metodě __new__. Tato metoda skutečně něco vytváří a není to klasická instanční, nýbrž statická metoda. Ale žádné obavy, je to automatické a nemusíme sami metodu dekorovat, že se jedná o statickou.

Metoda __new__ přijímá jeden parametr, a to třídu, kterou máme zkonstruovat. V této metodě vytvoříme novou instanci, upravíme, jak je třeba a poté vrátíme. Vrátit nově vytvořenou instanci je důležité! Jinak se defaultně vrátí (jak už to v Pythonu bývá) None hodnota a tím se instance nevytvoří, tím se ani nevyvolá metoda __init__ (zcela logicky) a prostě nic nemáme.

>>> class C(object):
...     def __new__(cls):
...         print "new"
...     def __init__(self):
...         print "init"
...     def f(self):
...         print "f"
...
>>> c = C()
new
>>> c.f()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'f'

# vs.

>>> class C(object):
...     def __new__(cls):
...         print "new"
...         return super(C, cls).__new__(cls)
...     def __init__(self):
...         print "init"
...     def f(self):
...         print "f"
...
>>> c = C()
new
init
>>> c.f()
f

Vnímavější může namítnout „ale vždyť v ukázce (SingletonMeta) jsou parametry čtyři!“ Nebojte, dostaneme se k tomu.

type

Co to je vlastně type? Je to buď funkce, která nám sděluje typ předávaného objektu, nebo to je standardní metatřída (něco jako object pro třídy), a nebo to je funkce, která nám umožňuje dynamicky vytvářet třídy. A nebo všechno dohromady.

>>> class C:
...     pass
...
>>> type(C())
<type 'instance'>
>>> type(C)
<type 'classobj'>

>>> class C(object):
...     pass
...
>>> type(C())
<class '__main__.C'>
>>> type(C)
<type 'type'>

>>> type(type)
<type 'type'>

Z ukázky je dobře patrná změna ze starých tříd na ty nové objektové a taky provázanost objectu s typem. Instance třídy C je typu třída C a třída C je typu metatřída type. Metametatřída už neexistuje, takže type zůstává  typem.

Zkusme si vytvořit třídu dynamicky (tj. ne přes klíčové slovo class). Použijeme, jak jsme si řekli, funkci type, jen nyní jako vstup použijeme víc a jiné parametry. Jako první předáme název třídy, poté tuple se seznamem, z čeho naše nová třída bude poděděna, a nakonec třídní slovník s atributy nové třídy.

>>> C = type('C', (), {'f': lambda self: 'Hello!'})
>>> c = C()
>>> c.f()
'Hello!'

Pokud se pozastavujete nad tím, proč se musí říct název, když stejně pak musím vytvořit proměnnou s referencí (když už ta proměnná má název), tak je to právě kvůli tomu. Proměnná je proměnná a třída je třída. Vždyť přece lze i normálně udělat toto:

class C(object):
    pass

D = C

Zde je taky proměnná D, ale třída se jmenuje C.

Demonstrace metatřídy

S touto znalostí se můžeme pustit do ukázky první metatřídy.

class M(type):
    def __new__(cls, classname, bases, classdict):
        return type.__new__(cls, classname, bases, classdict)

class C(object):
    __metaclass__ = M

Tato ukázka je vlastně zbytečná, protože takhle to funguje defaultně, pokud se dědí z  object u (samozřejmě se žádná M metatřída nevytváří, použije se type). Ale je to dobré pro ukázku, jak to vlastně pracuje. Každá třída poděděná z  object u má tak i nějakou metatřídu, nejčastěji type, která vlastně říká, jak se má třída vytvořit a chovat. Podobně jako to říká třída svým instancím.

Myslím, že dobře to lze pochopit taky takto: pomocí třídy se vytvářejí instance a pomocí metatřídy se vytvářejí třídy. Metoda __new__ ve třídě ( object) vytváří nové instance a metoda __new__ v metatřídě ( type) vytváří nové třídy. Není to úplně pravda, ale pro pochopení se to tak dá vysvětlit.

Analogicky funguje metoda __init__. Ve třídě ji všichni známe a v metatřídě přijímá 3 (resp. 4) parametry stejně jako výše popisovaná metoda  __new__.

Metametody

V metatřídě můžeme definovat metody (metametody), které potom získá i třída, která používá danou metatřídu. Instance se však k těmto metodám nedostane.

>>> class M(type):
...     def metamethod(cls):
...         return "metamethod of %s" % cls.__name__
...
>>> class C(object):
...     __metaclass__ = M
...     @classmethod
...     def classmethod(cls):
...         return "classmethod of %s" % cls.__name__
...
>>> c=C()

>>> print M.metamethod()
TypeError: unbound method must be called with M instance as first argument (got nothing instead)
>>> print M.metamethod(C)
metamethod of C
>>> print C.metamethod()
metamethod of C
>>> print c.metamethod()
AttributeError: 'C' object has no attribute 'metamethod'

>>> print C.classmethod()
classmethod of C
>>> print c.classmethod()
classmethod of C

Vhodná funkčnost se ukazuje většinou na srandičkách typu předefinování __str__ a ukážu to na tom také, protože jsem se zatím s jiným vhodným využitím nesetkal (nebo setkal, ale neuvědomil si to).

>>> class M(type):
...     def __str__(cls):
...         return '<trida %s>' % cls.__name__
...
>>> class C(object):
...     pass
...
>>> str(C)
"<class '__main__.C'>"
>>>
>>> class C(object):
...     __metaclass__ = M
...
>>> str(C)
'<trida C>'

Shrnutí definice

Pokud trochu tápete, prostě si pamatujte, že třídy jsou definice pro instance a metatřídy jsou definice pro třídy. Zbytek se dostaví časem samo.

Rozbor metatřídy  SingletonMeta

Nyní, když už máme potřebné informace, se už můžeme dát do rozboru metatřídy pro náš singleton.

Pro připomenutí, co bylo potřeba pro náš singleton ručně zařídit: definovat třídu singletonu (samozřejmě), v ní definovat sloty, aby nešlo jen tak všemožně editovat (nejlépe zakázat vše a zkonfigurovat při vytvoření), a definici přepsat jedinou instancí. Díky metatřídě tuto celou funkčnost můžeme zapouzdřit do znovupoužitelného kódu a na detaily nemyslet. A stačí na to jediná metoda.

Co vlastně ta metoda dělá? To co jsme si řekli. Definici hlavičky pravděpodobně mohu přeskočit, další řádek pouze vezme slovník s třídními atributy a nastaví mu nový atribut __slots__ s prázdným tuplem, aby nešlo nic nastavovat. Díky použití metody setdefault nebude případně již nastavený atribut přepsán. Na dalším řádku zavoláme skutečnou továrničku na třídy, nic extra. A v posledním kroku vytvoříme novou instanci nově vytvořené třídy a rovnou ji vrátíme. Díky tomu, že nevracíme nově vytvořenou třídu, ale rovnou její jednu instanci, získáme po napsání třídy s touto metatřídou instanci (a definice je skryta pouze v atributu __class__ naší nové instance).

A to je celé kouzlo. :-)

Komplikovanější problém

Abychom metatřídám porozuměli ještě lépe, zkusíme vyřešit jeden problém, který jsem nedávno řešil. Máme takovéto prostředí: aplikace, kde je hodně moc různých tříd. V té aplikaci je někde zapeklitý bug, který musíme najít. Je to tak zapeklité, že se nelze soustředit na určitou část kódu, poněvadž absolutně nechápeme, co naší aplikaci přeletělo přes nos (takový vzdálený příbuzný Skynetu) – tedy jednoduše si něco vypisovat nepomůže ( print, pprint, repr, …). Máme dobré logování, ale jako naschvál loguje pro tento konkrétní problém nešťastným způsobem. A klasický debugger nelze použít. Co teď? Jak si usnadnit práci s hledáním problému?

Můžeme si postupně zjišťovat, jakou cestou aplikace zpracovává naše požadavky a po té cestičce vyskládat nějaké záchytné body. A podle zjištění nějaké ubrat a nové přidat. A podle nového zjištění zase nějaké ubrat a přidat. A stále dokola, dokud se nenarazí na příčinu problému.

Nebo si pomoci trochu jinak…

Řekněme, že máme takovýto kód:

class C(object):
    def __init__(self):
        self.x = 'x'
        self.y = 'y'

    def func_with_bug(self):
        # ...

A teď bychom si chtěli logovat, co se v třídě děje. Například co se nastavuje za proměnné. To lze udělat metodou  __setattr__:

class C(object):
    def __setattr__(self, key, value):
        print "Nastavuju %s na %s." % (key, repr(value))
        super(C, self).__setattr__(key, value)

Tak, nyní se nám před nastavením hodnoty vypíše co se nastavuje a na co. Mohli bychom si třeba vypisovat i původní hodnotu… Dále by se nám hodilo zjišťovat, které proměnné se používají. To lze udělat pomocí metody  __getattribute__:

class C(object):
    def __getattribute__(self, key):
        print "Vracim %s." % key
        super(C, self).__getattribute__(key)

Pozor! Existují dvě metody, jmenují se velmi podobně, ale každá dělá něco jiného. Mluvím o zmíněné __getattribute__ a __getattr__. První ( __getattribute__) se zavolá vždy, ať už daný atribut existuje, či nikoliv, zato druhá ( __getattr__) se zavolá pouze, když hledaný atribut neexistuje.

>>> class C(object):
...     x = 'x'
...     def __getattribute__(self, key):
...         print 'getattribute', key
...         super(C, self).__getattribute__(key)
...     def __getattr__(self, key):
...         print 'getattr', key
...         super(C, self).__getattribute__(key)
...
>>> c = C()
>>> c.x
getattribute x
>>> c.y
getattribute y
getattr y
AttributeError: 'C' object has no attribute 'y'

V ukázce nemám chybu. object nemá metodu __getattr__, má pouze __getattribute__, která buď vrátí hodnotu atributu nebo vyhodí výjimku.

Dále by se mi líbilo logovat, které metody se volají, nejlépe s jakými parametry. Též není problém, od toho máme dekorátory:

def logdecorator(func):
    def wrapper(*args, **kwds):
        print 'Volani metody %s s parametry %s, %s' % (func.__name__, args, kwds)
        return func(*args, **kwds)
    return f

class C(object):
    @logdecorator
    def f(self):
        pass

    @logdecorator
    def g(self, a, b=2):
        pass

Nyní mám poměrně dobře pokrytou jednu třídu detailním logováním. Má to ale problém – vidíte, co se musí všechno nadefinovat a na co je potřeba myslet? To už je opravdu jednodušší si ručně rozházet logovací příkazy. Pojďme si to zjednodušit, tedy použít metatřídy. Víte, jak to napsat? V tom případě si to zkuste sami, než budete pokračovat…

Předělání na metatřídu

Začneme tím, že si vytvoříme prázdnou metatřídu s metodou __new__, ve které vytvoříme a vrátíme novou třídu.

class DebugMetaClass(type):
    def __new__(metacls, className, bases, classdict):
        cls = type.__new__(metacls, className, bases, classdict)
        return cls

Zatím nic extra, pouze zbytečný kód. Rozšiřme ho. Budeme postupovat ve stejném pořadí, takže přidáme naší metatřídě třídní metodu, která nám vytvoří výše ukázanou metodu logující setované hodnoty instancím nově vytvářené třídy a té třídě ji nastaví.

def __new__(...):
    ...
    cls.__setattr__ = metacls.create_set_attr_method(cls)
    ...

@classmethod
def create_set_attr_method(metacls, cls):
    def f(self, key, value):
        print "Nastavuju %s na %s." % (key, repr(value))
        super(cls, self).__setattr__(key, value)
    return f

Obdobně bychom vytvořili i metodu vytvářející metodu zaznamenávající, které proměnné našich instancí se používají. Co je ale složitější, je odekorování metod. Musíme projít všechny atributy a pouze callable atributy odekorovat. Menší kvíz: odekorovat metody před nebo za nastavením metod __setattr__ a __getattribute__? … Odpověď: Určitě před, protože kdybychom to dali za, tak bychom si tyto metody také odekorovali, což je zbytečné až nežádoucí.

def __new__(...):
    ...
    for attributename, attribute in classdict.items():
        if hasattr(attribute, '__call__'):
            dbgdecorator = metacls.create_debug_decorator(cls)
            setattr(cls, attributename, dbgdecorator(attribute))
    ...

@classmethod
def create_debug_decorator(metacls, cls):
    def f(func):
        def wrapper(*args, **kwds):
            print 'Volani metody %s s parametry %s, %s' % (func.__name__, args, kwds)
            return func(*args, **kwds)
        return wrapper
    return f

Pro ověření, zda je atribut metoda (nebo funkce), jsem naschvál použil zápis hasattr(attribute, '__call__') místo built-in funkce callable, protože chceme podporovat co nejvíce verzí Pythonu. Jinými slovy funkci callable v Pythonu 3.0 a 3.1 nenalezneme. Naštěstí vývojáři dostali rozum a v Pythonu 3.2 se vrací zpět.

Výsledek

Takovéto logování může být fajn, ale nemusí postačovat. Například by bylo dobré logovat dobu trvání a výsledek funkcí. Nebo odkud se s instancí pracuje. Vše je řešitelné a cílem této ukázky není řešit tyto drobnosti. Cílem je ukázat, jak jednoduše a kdy například lze použít metatřídy.

Jak jsem řekl v úvodu řešení logovacího problému: už jsem tento problém řešil. Kód už jsem tedy sepsal do znovupoužitelné podoby a umístil na GitHub. Také jsem ho přidal do PyPI. Nemusíte tak tento kód přepisovat do vašeho modulu a dořešit detaily, o kterých jsem mluvil o odstavec výše. Podívat se na kód můžete na https://github­.com/…thon-debugger a nainstalovat z PyPI příkazem  pypi-install debugger.

Závěr

Metatřídy se většinou jako řešení nějakého problému nehodí, ale jsou užitečné. Pomohou nám pochopit, jak Python pracuje a jednou za čas elegantně vyřešit problémy, které nejdou jednoduše řešit klasickou cestou. Další zajímavé použití metatříd je například v Djangu, kde se používají u databázových modelů, mrkněte se jim na kód (ale pozor, je to docela elektrárna). Další zajímavé čtení o metatřídách lze nalézt v mini seriálu na stránkách IBM s dalšími ukázkami, kde se dají metatřídy vhodně využít:

Dalším zajímavým zdrojem může být oficiální popis od Guido van Rossum v článku Unifying types and classes in Python 2.2.

A tím ukončuji naše povídání o zajímovstech z Pythonu. Netvrdím však, že jsem vám sdělil vše zajímavé a must know. Stále je o čem mluvit a kde se dál vzdělávat. Další spoustu informací lze najít v dokumentaci. Python dokáže vše nějak vyřešit a prohledání dokumentace (případně stackoverflow) mi vždy pomohlo. A pomůže určitě i vám. Stačí jen zkusit hledat, protože již napsaný modul v céčku od chytřejších lidí je vždy lepší, než si něco bastlit sám v Pythonu.

Dokumentace nejvíce používané poslední stabilní verze je na adrese http://docs.pyt­hon.org/ a určitě doporučuju si ji projít.

Komentáře

Subscribe
Upozornit na
guest
22 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
pavel s.

Fajn, když už známe mechanismus tříd, jak bychom implementovali datový typ Roman odvozený z typu int, který bude sloužit pro počítání s římskými čísly? (moje oblíbená otázka :-) )

class Roman(int): pass

assert Roman(‚VI‘) + Roman(‚I‘) == Roman(‚VII‘)

Doplňte tělo třídy Roman.

Ivan Nový

ale metatřídy můžete použít k vytvoření třídy instance, která zkontroluje zda vytvářená třída má implementovány všechny metody z třídy, kterou dědí, což je interface například z Javy a což Python nemá. Nebo metatřídy využívá framework Django k deklarativnímu programování modelů (MVC). Viz zde http://lanyrd.com/2011/pyconau/sfxwt/

pavel s.

Co je nesmysl?

Ivan Nový

Nesmysl je implementovat třídu Roman, když to můžete vyřešit konverzí na integer. Vytvoříte-li třídu Roman, je nebezpečí, že někdo z pohodlnosti ji bude používat a ani si neuvědomí, že počítání s ní je dost drahé, protože minimálně před každou a na konci každé operace musí dojít ke konverzi z a na integer :-)))

pavel s.

> Nesmysl je implementovat třídu Roman, když to můžete vyřešit konverzí na
integer.

Tohle se melo dit vevnitr tridy Roman, kde je momentalne „pass“. Ke konverzi dojde jen na zacatku (pri parsovani stringu). Aritmetika probehne jako s beznym integerem.

> Počítání s ní je dost drahé, protože minimálně před každou a na konci každé operace musí dojít ke konverzi z a na integer.

Pane, mate v tom trochu zmatek. Rozsiruji tedy svuj assert na toto, zadani zustava stejne:

assert Roman(‚VI‘) + Roman(‚I‘) == Roman(‚VII‘) == 7.

jen tak

class Roman(int):

@staticmethod
def roman_to_deci­mal(roman_num­ber):
„prevede rimske cislo na int“

def __new__(self, *args, **kwargs):
return int.__new__(self, Roman.roman_to_de­cimal(args[0]), **kwargs)

assert Roman(‚VI‘) + Roman(‚I‘) == Roman(‚VII‘) == 7

asi by to slo i lip, ale takhle to snad vystihuje myslenku

Opravdový odborník :-)

A neztrácí při takové implementaci využití třídy smysl? Jakou má výhodu volání konstruktoru Roman() místo přímého volání funkce roman_to_decimal()?

Pokud tomu dobře rozumím, tak výsledkem je pak obyčejný int. Nebo jak z něj dostanu např. převod zpět na římské? Půjde na něm zavolat nějaká metoda, která vrátí patřičný String, nebo budu muset tento int vložit jako parametr do jiné funkce? (případně opět do konstruktoru)

Zatím moc nevidím přínos oproti obyčejným funkcím roman_to_decimal() a decimal_to_roman() a tento příklad mi přijde takový vykonstruovaný a zbytečný.

P.S. V Javě máme pěknou implementaci, která umožňuje zapisovat římské číslice přímo do kódu (bez uvozovek, úplně stejně, jako píšeme arabské). Sice je to zbytečnost, praktické využití to nemá, ale je to alespoň technicky zajímavé řešení.

M747

V uvodnom priklade je pouzite meno s malym aj s velkym „D“.

Ivan Nový

….

FiŠ

Připadá mi divné neoznačovat __init__ jako konstruktor. Nevím, jestli se v Pythonu tradičně myslí pod pojmem konstruktor metoda __new__, jak je zmíněno v článku, ale třeba v C++ se podle mě pojmem konstruktor označuje přesně to, co dělá metoda __init__. Je to instanční metoda (má pointer this) a paměť pro danou instanci taky nealokuje. Tudíž __new__ bych z pohledu C++ označil jako přetížený operátor new :-).

Honza Kral

mista hasattr(xxx, ‚__call__‘) mame v pythonu funkci callable()
nepouzivejte prosim camelCase pro nazvy parametru, pep-8 stae existuje

ukazka kodu na create_set_at­tr_method je proste spatne:
* je totalne zbytecne generovat tu metodu dynamicky
* navic je to i spatne napsane – misto ‚C‘ tam ma byt cls
* I tak by to ale bylo spatne v pripade ze nekdo uz ma tu metodu pretizenou, pak mu proste rozbijete kod a zavedete do systemu nepreberne mnozstvi bugu

Plati rozumna zasada ze kdyz hledam bug tak se snazim do veci nezasahovat vic nez musim, takhle extenzivni zasah je naprosto kontraproduktivni – budu se opakovat – neni to testovatelne, vykonne ani citelne ale hlavne to neplni svoji funkci a je to spatne. Kdyz potrebuju tracovat co program dela, pouziju tracer, profiler, debugger nebo jakykoliv jiny nastroj ktery je k tomu urcen (treba coverage.py se hodi casto na takove veci), nebudu si obfuskovat kod sbirkou hacku ktera je navic (v tomto pripade) nejen zcela zcestna ale i fakticky spatne!

Spatny link na github uz je jen prkotina kterou si kazdy opravi.

Dalsi clanek ktery je jen nepreberna sbirka hacku za ktery by se kazdy python programator stydel.

yad

Na výkon to nebude mať veľký vplyv tak či onak. Triedy sa vytvárajú len raz.

Btw, čo sa týka hľadania chýb, tak tu je veľmi dobré si prečítať knihu Dokonalý kód.

Honza Kral

Za callable se omlouvam, prehledl jsem tu zminku.

Bez dynamickeho generovani:

def f(self, name):
print name
return object.__getat­tribute__(sel­f, name)
cls.__getattri­bute__ = f

f muze byt klidne funkce na urovni modulu, neni treba ji jakkoli generovat

Kdyz delas takovehle extenzivni zasahy do kodu kvuli debugovani, jakou mas jistotu ze si neco nerozbil kdyz na to nepustis testy? Copak takovehle upravy ktere ti potencialne zmeni chovani kazde metody a atributu netestujes? Ne, skutecne neni hloupost u takovychto veci mluvit o testovatelnosti. Minimalne je potreba vedet ze ti to nenarusilo funkcnost, idealne budes provaded debugovani primo za pomoci testu abys mel vse pod kontrolou a mel pak test kvuli potencialnim regresim.

Jinak bych ocenil alespon zminku o modulu logging – print neni uplne vhodny debug nastroj (buh vi kam v danou chvili smeruje sys.stdout) nehlede na moznosti ktere ti logging da a ktere v ukazkove knihovne implementujes sam. Mimochodem zkus tvoji knihovnu pouzit s kterymkoliv z nasledujicich scenaru
vlastni __setattr__
trida uz ma svou metaclasss (django model)
nedo zavola metodu a jako parametr ji da unicode
nastroj pracuje se stdout

Ano, vim ze spoustu mych namitek je nad ramec jednoho clanku, ale kdyz k tomu jeste nalinkujes ukazku na githubu kde jsou vsechny tyto chyby take (snad krome toho dyn. generovani mozna), uz to stoji za to to zminit aby si nekdo nahodou nemyslel ze takhle se to v pythonu dela nebo ma delat.

Nedokazu si predstavit ze by neslo pouzit tracer ci debugger ale je ok provest neco takoveho, osvetlis nam proc to tak bylo?

Honza Kral

sorry za formatovani, neuvedomil jsem si ze hvezdicky to rozhodi, neni to umyslne.

jen tak

Souhlasim,
a kdyz uz jsme u toho, hodil by se vycerpavajici clanek o testovani.

Martin Hassman

Souhlasím, až takové články napíšete, moc rádi je vydáme. 8-)

Honza Kral

bohuzel s psanim clanku je to u me tezke, to musim uznat ze v tomhle ma nade mnou autor jasnou prevahu.. kdysi jsem tu psal nejaky uvod do testovani, ted se snazim co to jde dopsat clanek o denormalizacich do redisu ktery uz jsem slibil na zdrojak.

Kdyby se ale nasel nekdo kdo umi psat a bavi ho to, rad jim poradim a pomuzu s technictejsi casti, pripadne se staci stavit na pravidelny python sraz (kazdou treti stredu v mesici na Smichove), udelame tam tematicky vecer nebo prednasku a pak z toho staci sepsat zapis.

Kazdemu zajemci ci zajemkyni rad zaplatim pivo ci napoj dle jejich vyberu :)

Ales Zoulek

> Mimochodem mluvit zde o testovatelnosti nebo vykonnosti je hloupost – kdo by to použil pro něco jiného, než debugování?

To nemyslis uplne vazne, ze ne?

Nebo snad knihovny urcene k debugovani nemaji testy? Zvlast pokud to sdilis na githubu a mohlo by hrozit, ze ti tam nekdo bude chtit neco doplnit? O tom, ze testy jsou hlavne hodne pohodlny na vlastni vyvoj ani nemluve…

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.