Magie

Co z Pythonu dělá tak užitečný jazyk? Z velké části je to zaměření na čitelnost kódu. Pythonu se někdy říká „spustitelný pseudokód“: syntaxe je inspirovaná zápisem abstraktních algoritmů v matematice, používá se málo speciálních znaků jako $, <<, &&, ?. Čitelnosti je podřízena expresivita (proto v Pythonu nenajdeme makra jako v Lispu) i délka zápisu (některé věci se nedají napsat na jeden řádek).

Návrh jazyka (a knihoven pro něj) se řídí mimo jiné poučkou „There should be one– and preferably only one –obvious way to do it.“ Existuje ideálně jeden zjevně nejlepší způsob, jak dosáhnout určité funkčnosti. Úkolem programátora je tento způsob najít a použít. Náš cíl by měl být kód, který ostatní programátoři pochopí na první pohled. A pokud je to možné, měli by ho pochopit i neprogramátoři. Není mezi nimi pevná hranice – váš kód můžou číst lidi, kteří programovací jazyk v životě neviděli; programátoři silní v jiných jazycích; průměrní Pythonisté; nebo ostřílení veteráni. Čím víc jich dokáže kód pochopit, tím bude váš kód udržovatelnější.

S tím souvisí koncept „magie“. Magie je něco, co funguje, ačkoli tomu nerozumíme. Pro každého čtenáře kódu může být magie něco jiného: pro začátečníka bude nepochopitelný zápis zip(*args), matematik nemusí chápat princip dědičnosti tříd, ostřílený Pythonista nemusí chápat maticovou matematiku, neprogramátor netuší, jak funguje mobil nebo webová aplikace. Je to tedy subjektivní pojem, ale lze ho zobjektivnit: čím méně lidí váš kód pochopí, tím je kód magičtější.

Magie, která funguje, nevadí. Věci které nechápu, můžu stále používat – jen nevím jak fungují, a tudíž je neumím opravit. Problém nastane až v momentě, kdy se něco pokazí.

Přehlednost a udržitelnost kódu je samozřejmě potřeba vyvážit s ostatními aspekty. Kód musí být například dostatečně rychlý a optimalizace ho často znepřehlední. Proto je dobré optimalizovat až potom, co vím, že je kód správný a řeší opravdu problém, který potřebuji vyřešit. V ten moment napíšu testy a potom, když je potřeba, můžu optimalizovat – trochu přehlednosti vyměnit za rychlost.

Na jiný důvod, proč použít méně pochopitelné techniky, narazíme při psaní knihoven a frameworků. To je kód, který používá hodně programátorů – a často jsou to programátoři s méně zkušenostmi, než mají autoři knihovny. Tady proto bývá dobré občas použít nějakou tu magii – znepřehlednit kód knihovny, aby kód který knihovnu používá, mohl být přehlednější.

Typický příklad jsou dekorátory ve Flasku. Napsat dekorátor není úplně triviální, ale velice to zjednodušuje práci všem, co ve Flasku píšou web. Konstrukce @app.route je pro většinu lidí magická – nevíme přesně, co to dělá, ale to nám nebrání ji použít.

Velká moc a velká zodpovědnost

Druhý možný význam slova magie je něco, co neodpovídá „normálnímu“ chování podobných věcí.

Jazyk Python standardizuje syntaxi relativně malého počtu operací (operátory, volání funkcí, atributy, ...), způsobů řízení toku programu (cykly, with, ...) a strukturování kódu (moduly, def, class). To, že je jich relativně málo, má dvě výhody: zaprvé fungují s ostatními části jazyka a zadruhé nebývá problém rozhodnout, který způsob je pro daný problém nejlepší.

Téměř všechno v Pythonu ale jde předefinovat. Operátor / nemusí jen dělit: můžu si napsat třídu pro jméno souboru, která umí pomocí / oddělovat adresáře *. Cyklus for nemusí iterovat přes předem danou sekvenci prvků; iterovatelný objekt může poskytovat jakékoli hodnoty podle jakýchkoli pravidel. Příkaz class dokonce vůbec nemusí vytvořit třídu, jak uvidíme později.

Ačkoliv si ale můžeme dovolit téměř cokoli, je dobré mít na paměti, že odchylky od „normálního“ chování jsou magické. Jakmile někdo použije divnou třídu, která předefinovává dělení, ve svém kódu, musí každý čtenář toho kódu nejen opustit představu o tom, co operátor / dělá, ale hlavně si předtím uvědomit, že / může dělat něco divného. To samé platí u „divných“ iterovatelných objektů nebo tříd. Odchylka od normálního chování, je-li nezbytná, by měla být dobře promyšlená a zdokumentovaná.

Nadefinujeme-li vlastní nestandardní – „magické“ – chování některých objektů, často se stane, že nebudou fungovat s ostatními prvky jazyka tak dobře, jako to co je zabudované. Předefinujeme-li < tak, že nebude mít nic společného s porovnáváním, bude se funkce sorted chovat podivně. Když u svých objektů předefinuji přístup k atributům, musím si dávat zvlášť pozor na to, aby fungovala funkce dir().

Následující principy (kromě jiných) je proto dobré při psaní knihoven používat jen po pečlivém zvážení, jestli by to nešlo i bez magie.

* Taková třída dokonce existuje ve standardní knihovně.

Speciální metody

Základní způsob, jak přizpůsobit chování objektů, jsou speciální metody. Asi už víte, že všechny atributy, které začínají a končí dvojitým podtržítkem, jsou rezervované pro samotný Python, který je používá podle svých pravidel – například danou metodu volá, když je potřeba sečíst dvě čísla.

Speciální metody jsou popsané v dokumentaci. Zde uvedu jen přehled, který pokročilý Pythonista nosí v hlavě, aby věděl co je všechno možné. Doporučuji si předtím, než nějakou naimplementujete, dokumentaci přečíst.

Metody pro předefinování aritmetických operátorů: __add__, __sub__, __mul__, __div__, __floordiv__, __pow__, __matmul__, __lshift__, __rshift__, __or__, __xor__ a varianty s r a i (__radd__, __iadd__, atd.); __neg__, __pos__, __abs__, __invert__.

Metody pro předefinování porovnávání: __eq__, __ne__, __lt__, __gt__, __le__, __ge__, __hash__.

Metoda pro zavolání objektu jako funkce: __call__.

Metody pro funkčnost sekvencí a kontejnerů: __len__, __iter__, __next__, __reversed__; __contains__ pro operátor in.

Metody pro „hranaté závorky“: __getitem__, __setitem__, __delitem__.

Převádění na řetězce: __repr__, __str__, __format__.

Převádění na čísla: __complex__, __float__, __index__, __round__, __floor__, __ceil__.

Převádění na bool (např. i v if): __bool__.

Vytváření a rušení objektů: __new__ (konstruktor – vytvoří objekt dané třídy), __init__ (inicializuje objekt dané třídy), __del__ (zavoláno před zrušením objektu).

Předefinování přístupu k atributům: __getattr__ (zavolá se, pokud se atribut nenajde), __getattribute__ (zavolá se pro každý přístup k atributu), __setattr__, __delattr__, __dir__.

Implementace context manageru (pro with): __enter__, __exit__.

Implementace deskriptoru (viz níže): __get__, __set__, __delete__.

Implementace asynchronní funkcionality: __await__, __aiter__, __anext__, __aenter__, __aexit__.

Předefinování hierarchie dědičnosti: __instancecheck__, __subclasscheck__.

Dekorátory

Další věc, na kterou se podíváme, jsou dekorátory – způsob, jak si přizpůsobovat funkce.

Nejjednodušší použití dekorátorů je registrace: k funkci přidáme dekorátor a funkce se někam zaregistruje, uloží, aby se dala zavolat později. Typický příklad je @app.route ve Flasku.

My si pro příklad budeme chtít udělat dekorátor pro kalkulačku, @register_operator, aby fungoval tento kód:

operators = {}

@register_operator
def add(a, b):
    return a + b

@register_operator
def mul(a, b):
    return a * b

a = int(input('First number: '))
operator_name = input('Operation: ')
b = int(input('Second number: '))

func = operators[operator_name]
print(func(a, b))

Bez použití dekorátorů by se to dalo napsat takto:

def register_operator(func):
    operators[func.__name__] = func

def add(a, b):
    return a + b

register_operator(add)

S použitím dekorátoru je funkce register_operator téměř stejná, jen použijeme speciální syntaxi se zavináčem.

def register_operator(func):
    operators[func.__name__] = func
    return func

@register_operator
def add(a, b):
    return a + b

Použití dekorátoru je jen zkrácený zápis pro volání dekorátoru jako funkce – poslední tři řádky předchozího příkladu jsou ekvivalentní tomuto:

def add(a, b):
    return a + b

add = register_operator(add)

Chování samotného @ je tedy celkem triviální. Magie (složitost) spočívá v tom, že dekorátor je většinou funkce vyššího řádu: bere jinou funkci jako argument a taky jinou funkci vrací. V případě registrace vrací stejnou funkci jako dostala – ale to není povinné.

Často se setkáme s dekorátory, které dekorovanou funkci nějak modifikují. Například můžeme napsat dekorátor, který v naší kalkulačce převede vstup na reálná čísla. Dělá to tak, že definuje novou funkci, která volá tu původní – ale před nebo po tomto volání může dělat i něco jiného.

def to_floats(func):
    def outer_function(a, b):
        a = float(a)
        b = float(b)
        return func(a, b)
    return outer_function

@to_floats
def add(a, b):
    """Adds two numbers"""
    return a + b

print(add(1, '2'))

Takto funguje většina dekorátorů, které mění chování dekorované funkce. Naráží s tím ale na jeden problém: nově nadefinovaná funkce má vlastní jméno (a dokumentační řetězec a podobné informace), což kazí iluzi, že jsme původní funkci jen trošku změnili:

print(add)
help(add)

Řešení je jednoduché – zkopírovat jméno, dokumentační řetězec atd. z jedné funkce na druhou. Na to ve standardní knihovně existuje dekorátor jménem functools.wraps:

import functools

def to_floats(func):
    @functools.wraps(func)
    def outer_function(a, b):
        a = float(a)
        b = float(b)
        return func(a, b)
    return outer_function

S wraps bude help(add) fungovat správně – ukáže původní jméno a dokumentační řetězec.

Z volání wraps(func) je vidět, že jako dekorátor můžeme použít i volání funkce, ne jen funkci samotnou. Budeme-li chtít napsat dekorátor, který tohle umí, potřebujeme napsat funkci ještě vyššího řádu – totiž funkci, která po zavolání vrátí dekorátor:

operators = {}

def register_operator(name):
    def decorator(func):
        operators[name] = func
        return func
    return decorator

@register_operator('+')
def add(a, b):
    return a + b

@register_operator('*')
def mul(a, b):
    return a * b

a = int(input('First number: '))
operator_name = input('Operation: ')
b = int(input('Second number: '))

func = operators[operator_name]
print(func(a, b))

Řádek @register_operator('+') dělá (jak už víme) to stejné, jako bychom hned za funkcí napsali add = register_operator('+')(add).

Budete-li chtít napsat dekorátor, který bere argumenty, a přitom ještě „mění“ dekorovanou funkci, dostanete se na tři funkce zanořené v sobě:

import functools
operators = {}

def register_operator(name):
    def to_floats(func):

        @functools.wraps(func)
        def outer_function(a, b):
            a = float(a)
            b = float(b)
            return func(a, b)

        operators[name] = outer_function
        return outer_function

    return to_floats

@register_operator('+')
def add(a, b):
    return a + b

func = operators['+']
print(func(1, '2'))

Dekorátorů se na jedné funkci dá použít víc:

@register_operator('×')
@register_operator('*')
def mul(a, b):
    return a * b

Úplně stejně jako funkce se dají dekorovat i třídy. Dekorátor dostane třídu jako první argument a třída se nahradí tím, co dekorátor vrátí.

Deskriptory

Jeden z nejmagičtějších operátorů v Pythonu je ., tečka. Je magický v obou významech – většina lidí ho používá, ačkoli nemá tušení, co přesně dělá, a dá se předefinovat tolika různými způsoby, že to vydá na celou přednášku.

Pomocí tečky zapisujeme tři operace: čtení atributu (print(foo.bar)), zapisování (foo.bar = 3) a mazání (del foo.bar). Tady se zaměříme hlavně na nejmagičtější z nich, čtení.

Kdykoli atribut čteme pomocí tečky, hledá se několika místech:

  • na samotné instanci objektu,
  • pokud se tam nenajde, tak na třídě,
  • pokud se nenajde ani tam, tak na rodičovských třídách (v případě vícenásobné dědičnosti podle MRO),
  • a pokud stále není k nalezení, vyhodí se AttributeError.

To je trochu zjednodušený, ale užitečný model.

Speciální metody, které se nevolají pomocí tečky, přeskakují první krok: metoda __add__ tedy musí být definována na třídě, aby se zavolala pro a + b.

(Poznámka navíc pro ty, kdo čtou tento text podruhé: na metatřídě se atribut nehledá; např. existuje-li type.mro, najde se str.mro, ale už ne "".mro)

Podívejme se teď na získávání atributu trošku podrobněji. Je to poměrně komplikovaný proces a existuje několik způsobů, jak ho přizpůsobit. Nejjednodušší je dvojice speciálních metod:

  • __getattribute__, která kompletně předefinuje funkci . pro čtení atributu, a
  • __getattr__, která se zavolá, až když se atribut nenajde normálním způsobem.

První z nich nedoporučuji používat, protože je příliš obecná (pokusy se z ní dostat ke stavu objektu končívají nekonečnou rekurzí). Příklad druhé:

class Palette:
    red = 255, 0, 0
    green = 0, 255, 0

    def __getattr__(self, attr_name):
        prefix, sep, suffix = attr_name.partition('_')
        if prefix == 'dark':
            original_color = getattr(self, suffix)
            return tuple(c//2 for c in original_color)
        else:
            raise AttributeError(attr_name)

palette = Palette()
print(palette.dark_red)

(Předpokládám že znáte funkci getattr; kdyby ne: getattr(foo, "bar") dělá totéž co foo.bar – jen je jméno atributu předáno jako řetězec, takže může být např. v proměnné. Podobně existují setattr(instance, attr_name, new_value) a delattr(setattr(instance, attr_name).)

Metoda __getattr__ je většinou tak trochu kanón na vrabce: ve většině případů nepotřebujeme nastavit chování všech neexistujících atributů, ale jenom jednoho nebo několika konkrétních. Například máme třídu pro 2D bod s atributy x a y a potřebujeme i atribut pro dvojici (x, y). Toto se často dělá pomocí dekorátoru property:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @property
    def pos(self):
        return self.x, self.y

point = Point(41, 8)
print(point.pos)

Jak to ale funguje? Dekorátor property je třída, jakou můžete teoreticky napsat sami v Pythonu. Je to deskriptor, objekt, který v rámci nějaké třídy popisuje jak přistupovat k nějakému atributu.

Nejlépe se deskriptory vysvětlí na příkladu:

# (Omluvte prosím češtinu v kódu)

class Descriptor2D:
    """Popisuje atribut, který kombinuje dva jiné atributy do dvojice"""

    def __init__(self, name1, name2):
        self.name1 = name1
        self.name2 = name2

    def __get__(self, instance, cls=None):
        """Volá se, když je třeba načíst atribut dané `instance` na dané třídě `cls`.
        """

        if instance is not None:
            # Je-li instance nastavena, čteme atribut z ní.
            return getattr(instance, self.name1), getattr(instance, self.name2)
        else:
            # Je-li instance None, čteme atribut přímo ze třídy `cls`;
            # v tomto případě slušné deskriptory většinou vrací deskriptor samotný.
            return self

class Rect:
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

    pos = Descriptor2D('x', 'y')
    size = Descriptor2D('w', 'h')

rect = Rect(1, 2, 3, 4)
print(rect.pos)
print(rect.size)

# Čtení atributu přímo ze třídy:
print(Rect.pos)

Deskriptory jsou tedy součást třídy – atributy s nějakým jménem. Popisují, jak se bude přistupovat k atributu daného jména.

Existují dva druhy deskriptorů: data descriptor a non-data descriptor. Liší se v tom, jestli popisují jen, jak se daný atribut čte, nebo i jak se do něj zapisuje. Výše uvedený deskriptor je non-data: ovládá jen čtení. Zápis funguje jako u normálních atributů: přepíše aktuální hodnotu – a nová hodnota se pak použije místo volání deskriptoru:

rect.pos = 'haha'
print(rect.pos)

Abychom tomu zabránili, můžeme na deskriptoru nadefinovat speciální metodu __set__ (nebo __delete__), která popisuje, jak se atribut nastavuje (resp. maže). Tím vznikne data descriptor:

class Descriptor2D:
    def __init__(self, name1, name2):
        self.name1 = name1
        self.name2 = name2

    def __get__(self, instance, cls=None):
        if instance is not None:
            return getattr(instance, self.name1), getattr(instance, self.name2)
        else:
            return self

    def __set__(self, instance, new_value):
        a, b = new_value
        setattr(instance, self.name1, a)
        setattr(instance, self.name2, b)

    def __delete__(self, instance):
        delattr(instance, self.name1)
        delattr(instance, self.name2)

class Rect:
    # jako předtím

rect = Rect(1, 2, 3, 4)
rect.pos = 123, 456
print(rect.pos)

Už zmíněný vestavěný deskriptor property je data descriptor. Popisuje jak čtení, tak zápis atributu. Pokud mu nenastavíme funkci pro zápis, vyhodí ze své metody __set__ výjimku AttributeError se zprávou, že do atributu se zapisovat nedá. (To je trochu magická odchylka od normálního chování Pythonu, kdy atributy zapisovat jdou.)

Nejčastější příklad non-data deskriptoru je obyčejná funkce. Každá funkce totiž funguje jako deskriptor: má speciální metodu __get__, která zajišťuje, že pokud je nastavena na třídě, daným atributem nedostaneme funkci, ale metodu (s „předvyplněným“ parametrem self).

def foo(self):
    return 4

class C:
    foo = foo

c = C()

# Obyčejná funkce
print(C.foo)
print(foo)

# Metoda
print(C().foo)
print(foo.__get__(c))

Protože je to non-data deskriptor, můžeme v jednotlivých instancích třídy daný atribut přepsat něčím jiným, čímž metodu znepřístupníme.

Jako zajímavost uvedu non-data deskriptor, který přepisuje svůj vlastní atribut. Funguje podobně jako @property, jen se výsledek vypočítá pouze jednou a uloží se jako normální atribut. Při dalším přístupu k atributu už se použije uložená hodnota.

class reify:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls=None):
        if instance is None:
            return self
        val = self.func(instance)
        setattr(instance, self.func.__name__, val)
        return val

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @reify
    def length(self):
        print('Running expensive computation...')
        return (self.x ** 2 + self.y ** 2) ** 0.5

vect = Vector(3, 4)
print(vect.length)
print(vect.length)
print(vect.length)

Kompletní implementace je např. ve frameworku Pyramid jako pyramid.decorator.reify.

Konstruktor

Třídy v Pythonu můžou mít konstruktor – funkci, která se zavolá, aby vytvořila objekt daného typu. Toto není známá metoda __init__ – ta objekt nevytváří, ta dostane už předpřipravený self, který jen naplní atributy. Opravdový konstruktor se jmenuje __new__ a chová se jako classmethod: místo self bere třídu, jejíž instanci má vytvořit.

Opravdový konstruktor se „hodí“ pro vytváření singletonů, tříd, které mají jen jednu instanci:

class Singleton:
    def __new__(cls):
        try:
            return cls._instance
        except AttributeError:
            cls._instance = super().__new__(cls)
            return cls._instance

assert Singleton() is Singleton()

Podobný trik lze použít pro třídu podobnou bool, která má pouze dvě instance: bool(1) is bool(2).

Metoda __new__ se hodí, když chceme dědit z neměnitelné (immutable) třídy jako tuple. Metoda __init__ sice dostane self, ale cokoli z nadtřídy už nemůže měnit. Je ale možné předefinovat __new__.

Normálně bere tuple jediný argument, tuple([1, 2]). Chceme-li brát dva, dá se to udělat takto:

class Point(tuple):
    def __new__(cls, x, y):
        return super().__new__(cls, (x, y))

print(Point(3, 4))

Metatřídy

Poslední věc, na kterou se podíváme, jsou metatřídy.

Začneme zlehka: pokud při definici třídy zadáme nějakou funkci jako pojmenovaný parametr metaclass, funkce se zavolá s informacemi potřebnými pro vytvoření třídy. Ty můžeme použít, nebo úplně ignorovat a vrátit něco jiného:

def fake_metaclass(name, bases, namespace):
    return 42

class NotAClass(metaclass=fake_metaclass):
    pass

print(NotAClass)

Argumenty, které „metatřída” dostane, jsou tři: jméno třídy, n-tice nadtříd a jmenný prostor – slovník s proměnnými, které vznikly vykonáním těla příkazu class. (Ve jmenném prostoru jsou implicitně nastavené záznamy __module__ a __qualname__, které přidává samotný příkaz class.)

def fake_metaclass(name, bases, namespace):
    print('name:', name)
    print('bases:', bases)
    print('namespace:', namespace)
    return 42

class NotAClass(int, metaclass=fake_metaclass):
    foo = 123
    def inc(self):
        return self + 1

Když metaclass nezadáme, použije se výchozí metatřída, tedy třída třídy. V Pythonu je to type. Pokud ji zavoláme s vhodnými argumenty, dostaneme normální třídu:

MyInt = type('MyInt', (int, ), {'foo': 123, 'inc': lambda self: self + 1})

three = MyInt(3)
print(three.inc())

Kromě toho se type dá zavolat i s jedním argumentem; v tom případě vrátí typ (třídu) daného argumentu. (Tohle chování – funkce, která dělá úplně různé věci v závislosti na počtu argumentů – v Pythonu často nevidíme. Je to nešťastná výjimka, která přežívá z historických důvodů.)

Pojďme se podívat na třídy několika základních objektů:

# Třída základních objektů
print(type(1))
print(type("abc"))

# Třída třídy – metatřída.
# Třída většiny tříd v Pythonu je `type`
print(type(int))
print(type(type(1)))

# Třída třídy třídy
# Samotná `type` je jedna z té většiny tříd; její třída je `type`
print(type(type))
print(type(type(type(1))))

Objekty třídy type (tedy třídy) se normálně tvoří příkazem class. Explicitně to můžeme napsat takto:

class NormalClass(metaclass=type):
    foo = 123

Když budeme chtít chování třídy změnit, budeme postupovat podobně jako u jiných objektů. Kdybych chtěl celé číslo, přes které jde iterovat, podědím z int a předefinuji __iter__. Pokud chci třídu, přes kterou jde iterovat (tedy ne přes objekty dané třídy – přes třídu samotnou!), podědím z type a předefinuji __iter__:

class IterableMeta(type):
    def __init__(cls, name, bases, namespace):
        cls.items = sorted(n for n in namespace
                           if not n.startswith('__'))
        super().__init__(name, bases, namespace)

    def __iter__(cls):
        return iter(cls.items)

class SimpleEnum(metaclass=IterableMeta):
    a = 1
    b = 2
    c = 3
    d = 4

print(SimpleEnum.a)
print(list(SimpleEnum))

(V metatřídě se většinou používá cls místo self, aby bylo jasné, že instance, se kterou pracujeme, je třída – ale to je jen konvence.)

Metatřídy se dědí. Pokud v příkazu class nezadám explicitně metaclass, použije se metatřída nadtřídy:

class AnotherEnum(SimpleEnum):
    x = 10
    y = 20
    z = 30

print(AnotherEnum.a)
print(list(AnotherEnum))

Tímto způsobem lze vnuknout třídám magické schopnosti bez toho, aby uživatel naší knihovny musel použít metaclass – stačí mu podědit z námi připravené třídy.

Další věc, kterou metatřídy umí, je připravit počáteční jmenný prostor. Metoda __init__ (nebo __new__) v metatřídě normálně dostane slovník, což nemusí být vždy to, co potřebuji. Můžu si chtít třeba „zapamatovat” pořadí, v jakém byly jednotlivé atributy vytvořeny – a slovník toto pořadí neuchovává.

Na to existuje speciální metoda __prepare__, která se, když na metatřídě existuje, zavolá pro vytvoření jmenného prostoru:

from collections import OrderedDict

class OrderRememberingMeta(type):
    def __prepare__(cls, name):
        return OrderedDict()

    def __init__(cls, name, bases, namespace):
        cls.items = list(namespace)
        super().__init__(name, bases, namespace)

    def __iter__(cls):
        return iter(cls.items)

class OrderedEnum(metaclass=OrderRememberingMeta):
    first = 1
    second = 2
    third = 3
    fourth = 4
    fifth = 5

print(list(OrderedEnum))

Toho se dá využít třeba v mapování objektů na databázi (např. v Django Models nebo SQLAlchemy), kdy chceme, aby pořadí sloupců tabulky odpovídalo tomu, jak jsou sloupce/atributy nadefinovány ve třídě.

A další

Další (bohužel?) oblíbený trik je vnuknutí magických schopností modulu.

Naimportované moduly Python ukládá do slovníku sys.modules, aby při dalším importu nemusel načítat znovu – sys.modules tedy slouží jako cache. A tuto cache můžeme změnit (tzv. cache poisoning) – přidat si do ní vlastní „modul“, který ovšem vůbec nemusí být modul, a tudíž může umět věci, které moduly normálně neumí:

import sys

sys.modules['fake'] = 'a string'

...

import fake

print(fake[2])

Když toto uděláme přímo z modulu, uživatel naší knihovny dostane podstrčený objekt hned při prvním importu. K tomu se hodí proměnná __name__, jméno aktuálního modulu:

sys.modules[__name__] = ReplacementModule()

Jiný trik je registrace „built-in“ („superglobální”) proměnné:

import builtins
builtins.ANSWER = 42

...

# Třeba v jiném modulu
print(ANSWER)

Tímto způsobem se dají i předefinovat vestavěné funkce, což může být někdy užitečné pro ladění. V produkčním kódu to ale, prosím, nedělejte.

Úkol

Úkol není!

Budete-li chtít některé techniky z této lekce ve svém kódu (včetně semestrálky) použít, zamyslete se, jestli se problém nedá vyřešit jednodušeji, čitelněji, přehledněji, udržovatelněji. Dobrý mág ví, kdy magii nepoužít.

{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2017/mipyt-zima:magic:0",
      "title": "Magie",
      "html": "\n          \n    \n\n    <h1>Magie</h1>\n<p>Co z Pythonu d&#x11B;l&#xE1; tak u&#x17E;ite&#x10D;n&#xFD; jazyk?\nZ velk&#xE9; &#x10D;&#xE1;sti je to zam&#x11B;&#x159;en&#xED; na &#x10D;itelnost k&#xF3;du. Pythonu se n&#x11B;kdy &#x159;&#xED;k&#xE1; &#x201E;spustiteln&#xFD; pseudok&#xF3;d&#x201C;: syntaxe je inspirovan&#xE1; z&#xE1;pisem abstraktn&#xED;ch algoritm&#x16F; v matematice, pou&#x17E;&#xED;v&#xE1; se m&#xE1;lo speci&#xE1;ln&#xED;ch znak&#x16F; jako <code>$</code>, <code>&lt;&lt;</code>, <code>&amp;&amp;</code>, <code>?</code>.\n&#x10C;itelnosti je pod&#x159;&#xED;zena expresivita (proto v Pythonu nenajdeme makra jako v Lispu) i d&#xE9;lka z&#xE1;pisu (n&#x11B;kter&#xE9; v&#x11B;ci se nedaj&#xED; napsat na jeden &#x159;&#xE1;dek).</p>\n<p>N&#xE1;vrh jazyka (a knihoven pro n&#x11B;j) se &#x159;&#xED;d&#xED; mimo jin&#xE9; pou&#x10D;kou &#x201E;There should be one&#x2013; and preferably only one &#x2013;obvious way to do it.&#x201C;\nExistuje ide&#xE1;ln&#x11B; jeden <em>zjevn&#x11B; nejlep&#x161;&#xED;</em> zp&#x16F;sob, jak dos&#xE1;hnout ur&#x10D;it&#xE9; funk&#x10D;nosti.\n&#xDA;kolem program&#xE1;tora je tento zp&#x16F;sob naj&#xED;t a pou&#x17E;&#xED;t. N&#xE1;&#x161; c&#xED;l by m&#x11B;l b&#xFD;t k&#xF3;d, kter&#xFD; ostatn&#xED; program&#xE1;to&#x159;i pochop&#xED; na prvn&#xED; pohled. A pokud je to mo&#x17E;n&#xE9;, m&#x11B;li by ho pochopit i neprogram&#xE1;to&#x159;i. Nen&#xED; mezi nimi pevn&#xE1; hranice &#x2013; v&#xE1;&#x161; k&#xF3;d m&#x16F;&#x17E;ou &#x10D;&#xED;st lidi, kte&#x159;&#xED; programovac&#xED; jazyk v &#x17E;ivot&#x11B; nevid&#x11B;li; program&#xE1;to&#x159;i siln&#xED; v jin&#xFD;ch jazyc&#xED;ch; pr&#x16F;m&#x11B;rn&#xED; Pythonist&#xE9;; nebo ost&#x159;&#xED;len&#xED; veter&#xE1;ni. &#x10C;&#xED;m v&#xED;c jich dok&#xE1;&#x17E;e k&#xF3;d pochopit, t&#xED;m bude v&#xE1;&#x161; k&#xF3;d udr&#x17E;ovateln&#x11B;j&#x161;&#xED;.</p>\n<p>S t&#xED;m souvis&#xED; koncept &#x201E;magie&#x201C;. Magie je n&#x11B;co, co funguje, a&#x10D;koli tomu nerozum&#xED;me. Pro ka&#x17E;d&#xE9;ho &#x10D;ten&#xE1;&#x159;e k&#xF3;du m&#x16F;&#x17E;e b&#xFD;t magie n&#x11B;co jin&#xE9;ho: pro za&#x10D;&#xE1;te&#x10D;n&#xED;ka bude nepochopiteln&#xFD; z&#xE1;pis <code>zip(*args)</code>, matematik nemus&#xED; ch&#xE1;pat princip d&#x11B;di&#x10D;nosti t&#x159;&#xED;d, ost&#x159;&#xED;len&#xFD; Pythonista nemus&#xED; ch&#xE1;pat maticovou matematiku, neprogram&#xE1;tor netu&#x161;&#xED;, jak funguje mobil nebo webov&#xE1; aplikace. Je to tedy subjektivn&#xED; pojem, ale lze ho zobjektivnit: &#x10D;&#xED;m m&#xE9;n&#x11B; lid&#xED; v&#xE1;&#x161; k&#xF3;d pochop&#xED;, t&#xED;m je k&#xF3;d magi&#x10D;t&#x11B;j&#x161;&#xED;.</p>\n<p>Magie, kter&#xE1; <em>funguje</em>, nevad&#xED;. V&#x11B;ci kter&#xE9; nech&#xE1;pu, m&#x16F;&#x17E;u st&#xE1;le pou&#x17E;&#xED;vat &#x2013; jen nev&#xED;m jak funguj&#xED;, a tud&#xED;&#x17E; je neum&#xED;m <em>opravit</em>. Probl&#xE9;m nastane a&#x17E; v moment&#x11B;, kdy se n&#x11B;co pokaz&#xED;.</p>\n<p>P&#x159;ehlednost a udr&#x17E;itelnost k&#xF3;du je samoz&#x159;ejm&#x11B; pot&#x159;eba vyv&#xE1;&#x17E;it s ostatn&#xED;mi aspekty. K&#xF3;d mus&#xED; b&#xFD;t nap&#x159;&#xED;klad dostate&#x10D;n&#x11B; rychl&#xFD; a optimalizace ho &#x10D;asto znep&#x159;ehledn&#xED;. Proto je dobr&#xE9; optimalizovat a&#x17E; potom, co v&#xED;m, &#x17E;e je k&#xF3;d <em>spr&#xE1;vn&#xFD;</em> a &#x159;e&#x161;&#xED; opravdu probl&#xE9;m, kter&#xFD; pot&#x159;ebuji vy&#x159;e&#x161;it. V ten moment nap&#xED;&#x161;u testy a potom, kdy&#x17E; je pot&#x159;eba, m&#x16F;&#x17E;u optimalizovat &#x2013; trochu p&#x159;ehlednosti vym&#x11B;nit za rychlost.</p>\n<p>Na jin&#xFD; d&#x16F;vod, pro&#x10D; pou&#x17E;&#xED;t m&#xE9;n&#x11B; pochopiteln&#xE9; techniky, naraz&#xED;me p&#x159;i psan&#xED; knihoven a framework&#x16F;. To je k&#xF3;d, kter&#xFD; pou&#x17E;&#xED;v&#xE1; hodn&#x11B; program&#xE1;tor&#x16F; &#x2013; a &#x10D;asto jsou to program&#xE1;to&#x159;i s m&#xE9;n&#x11B; zku&#x161;enostmi, ne&#x17E; maj&#xED; auto&#x159;i knihovny. Tady proto b&#xFD;v&#xE1; dobr&#xE9; ob&#x10D;as pou&#x17E;&#xED;t n&#x11B;jakou tu magii &#x2013; znep&#x159;ehlednit k&#xF3;d knihovny, aby k&#xF3;d kter&#xFD; knihovnu <em>pou&#x17E;&#xED;v&#xE1;</em>, mohl b&#xFD;t p&#x159;ehledn&#x11B;j&#x161;&#xED;.</p>\n<p>Typick&#xFD; p&#x159;&#xED;klad jsou dekor&#xE1;tory ve Flasku. Napsat dekor&#xE1;tor nen&#xED; &#xFA;pln&#x11B; trivi&#xE1;ln&#xED;, ale velice to zjednodu&#x161;uje pr&#xE1;ci v&#x161;em, co ve Flasku p&#xED;&#x161;ou web. Konstrukce <code>@app.route</code> je pro v&#x11B;t&#x161;inu lid&#xED; magick&#xE1; &#x2013; nev&#xED;me p&#x159;esn&#x11B;, co to d&#x11B;l&#xE1;, ale to n&#xE1;m nebr&#xE1;n&#xED; ji pou&#x17E;&#xED;t.</p>\n<h2>Velk&#xE1; moc a velk&#xE1; zodpov&#x11B;dnost</h2>\n<p>Druh&#xFD; mo&#x17E;n&#xFD; v&#xFD;znam slova <em>magie</em> je n&#x11B;co, co neodpov&#xED;d&#xE1; &#x201E;norm&#xE1;ln&#xED;mu&#x201C; chov&#xE1;n&#xED; podobn&#xFD;ch v&#x11B;c&#xED;.</p>\n<p>Jazyk Python standardizuje syntaxi relativn&#x11B; mal&#xE9;ho po&#x10D;tu operac&#xED; (oper&#xE1;tory, vol&#xE1;n&#xED; funkc&#xED;, atributy, ...), zp&#x16F;sob&#x16F; &#x159;&#xED;zen&#xED; toku programu (cykly, <code>with</code>, ...) a strukturov&#xE1;n&#xED; k&#xF3;du (moduly, <code>def</code>, <code>class</code>).\nTo, &#x17E;e je jich relativn&#x11B; m&#xE1;lo, m&#xE1; dv&#x11B; v&#xFD;hody: zaprv&#xE9; funguj&#xED; s ostatn&#xED;mi &#x10D;&#xE1;sti jazyka a zadruh&#xE9; neb&#xFD;v&#xE1; probl&#xE9;m rozhodnout, kter&#xFD; zp&#x16F;sob je pro dan&#xFD; probl&#xE9;m nejlep&#x161;&#xED;.</p>\n<p>T&#xE9;m&#x11B;&#x159; v&#x161;echno v Pythonu ale jde p&#x159;edefinovat. Oper&#xE1;tor <code>/</code> nemus&#xED; jen d&#x11B;lit: m&#x16F;&#x17E;u si napsat t&#x159;&#xED;du pro jm&#xE9;no souboru, kter&#xE1; um&#xED; pomoc&#xED; <code>/</code> odd&#x11B;lovat adres&#xE1;&#x159;e *.\nCyklus <code>for</code> nemus&#xED; iterovat p&#x159;es p&#x159;edem danou sekvenci prvk&#x16F;; iterovateln&#xFD; objekt m&#x16F;&#x17E;e poskytovat jak&#xE9;koli hodnoty podle jak&#xFD;chkoli pravidel.\nP&#x159;&#xED;kaz <code>class</code> dokonce v&#x16F;bec nemus&#xED; vytvo&#x159;it t&#x159;&#xED;du, jak uvid&#xED;me pozd&#x11B;ji.</p>\n<p>A&#x10D;koliv si ale m&#x16F;&#x17E;eme dovolit t&#xE9;m&#x11B;&#x159; cokoli, je dobr&#xE9; m&#xED;t na pam&#x11B;ti, &#x17E;e odchylky od &#x201E;norm&#xE1;ln&#xED;ho&#x201C; chov&#xE1;n&#xED; jsou <em>magick&#xE9;</em>. Jakmile n&#x11B;kdo pou&#x17E;ije divnou t&#x159;&#xED;du, kter&#xE1; p&#x159;edefinov&#xE1;v&#xE1; d&#x11B;len&#xED;, ve sv&#xE9;m k&#xF3;du, mus&#xED; ka&#x17E;d&#xFD; &#x10D;ten&#xE1;&#x159; toho k&#xF3;du nejen opustit p&#x159;edstavu o tom, co oper&#xE1;tor <code>/</code> d&#x11B;l&#xE1;, ale hlavn&#x11B; si p&#x159;edt&#xED;m uv&#x11B;domit, &#x17E;e <code>/</code> <em>m&#x16F;&#x17E;e</em> d&#x11B;lat n&#x11B;co divn&#xE9;ho. To sam&#xE9; plat&#xED; u &#x201E;divn&#xFD;ch&#x201C; iterovateln&#xFD;ch objekt&#x16F; nebo t&#x159;&#xED;d. Odchylka od norm&#xE1;ln&#xED;ho chov&#xE1;n&#xED;, je-li nezbytn&#xE1;, by m&#x11B;la b&#xFD;t dob&#x159;e promy&#x161;len&#xE1; a zdokumentovan&#xE1;.</p>\n<p>Nadefinujeme-li vlastn&#xED; nestandardn&#xED; &#x2013; &#x201E;magick&#xE9;&#x201C; &#x2013; chov&#xE1;n&#xED; n&#x11B;kter&#xFD;ch objekt&#x16F;, &#x10D;asto se stane, &#x17E;e nebudou fungovat s ostatn&#xED;mi prvky jazyka tak dob&#x159;e, jako to co je zabudovan&#xE9;. P&#x159;edefinujeme-li <code>&lt;</code> tak, &#x17E;e nebude m&#xED;t nic spole&#x10D;n&#xE9;ho s porovn&#xE1;v&#xE1;n&#xED;m, bude se funkce <code>sorted</code> chovat podivn&#x11B;. Kdy&#x17E; u sv&#xFD;ch objekt&#x16F; p&#x159;edefinuji p&#x159;&#xED;stup k atribut&#x16F;m, mus&#xED;m si d&#xE1;vat zvl&#xE1;&#x161;&#x165; pozor na to, aby fungovala funkce <code>dir()</code>.</p>\n<p>N&#xE1;sleduj&#xED;c&#xED; principy (krom&#x11B; jin&#xFD;ch) je proto dobr&#xE9; p&#x159;i psan&#xED; knihoven pou&#x17E;&#xED;vat jen po pe&#x10D;liv&#xE9;m zv&#xE1;&#x17E;en&#xED;, jestli by to ne&#x161;lo i bez magie.</p>\n<p>* <em>Takov&#xE1; t&#x159;&#xED;da dokonce <a href=\"https://docs.python.org/3/library/pathlib.html\">existuje ve standardn&#xED; knihovn&#x11B;</a>.</em></p>\n<h2>Speci&#xE1;ln&#xED; metody</h2>\n<p>Z&#xE1;kladn&#xED; zp&#x16F;sob, jak p&#x159;izp&#x16F;sobit chov&#xE1;n&#xED; objekt&#x16F;, jsou <em>speci&#xE1;ln&#xED; metody</em>.\nAsi u&#x17E; v&#xED;te, &#x17E;e v&#x161;echny atributy, kter&#xE9; za&#x10D;&#xED;naj&#xED; a kon&#x10D;&#xED; dvojit&#xFD;m podtr&#x17E;&#xED;tkem, jsou rezervovan&#xE9; pro samotn&#xFD; Python, kter&#xFD; je pou&#x17E;&#xED;v&#xE1; podle sv&#xFD;ch pravidel &#x2013; nap&#x159;&#xED;klad danou metodu vol&#xE1;, kdy&#x17E; je pot&#x159;eba se&#x10D;&#xED;st dv&#x11B; &#x10D;&#xED;sla.</p>\n<p>Speci&#xE1;ln&#xED; metody jsou popsan&#xE9; v <a href=\"https://docs.python.org/3/reference/datamodel.html#special-method-names\">dokumentaci</a>. Zde uvedu jen p&#x159;ehled, kter&#xFD; pokro&#x10D;il&#xFD; Pythonista nos&#xED; v hlav&#x11B;, aby v&#x11B;d&#x11B;l co je v&#x161;echno mo&#x17E;n&#xE9;.\nDoporu&#x10D;uji si p&#x159;edt&#xED;m, ne&#x17E; n&#x11B;jakou naimplementujete, dokumentaci p&#x159;e&#x10D;&#xED;st.</p>\n<p>Metody pro p&#x159;edefinov&#xE1;n&#xED; aritmetick&#xFD;ch oper&#xE1;tor&#x16F;:\n<code>__add__</code>, <code>__sub__</code>, <code>__mul__</code>, <code>__div__</code>, <code>__floordiv__</code>, <code>__pow__</code>, <code>__matmul__</code>, <code>__lshift__</code>, <code>__rshift__</code>, <code>__or__</code>, <code>__xor__</code> a varianty s <code>r</code> a <code>i</code> (<code>__radd__</code>, <code>__iadd__</code>, atd.);\n<code>__neg__</code>, <code>__pos__</code>, <code>__abs__</code>, <code>__invert__</code>.</p>\n<p>Metody pro p&#x159;edefinov&#xE1;n&#xED; porovn&#xE1;v&#xE1;n&#xED;:\n<code>__eq__</code>, <code>__ne__</code>, <code>__lt__</code>, <code>__gt__</code>, <code>__le__</code>, <code>__ge__</code>, <code>__hash__</code>.</p>\n<p>Metoda pro zavol&#xE1;n&#xED; objektu jako funkce:\n<code>__call__</code>.</p>\n<p>Metody pro funk&#x10D;nost sekvenc&#xED; a kontejner&#x16F;:\n<code>__len__</code>, <code>__iter__</code>, <code>__next__</code>, <code>__reversed__</code>; <code>__contains__</code> pro oper&#xE1;tor <code>in</code>.</p>\n<p>Metody pro &#x201E;hranat&#xE9; z&#xE1;vorky&#x201C;:\n<code>__getitem__</code>, <code>__setitem__</code>, <code>__delitem__</code>.</p>\n<p>P&#x159;ev&#xE1;d&#x11B;n&#xED; na &#x159;et&#x11B;zce:\n<code>__repr__</code>, <code>__str__</code>, <code>__format__</code>.</p>\n<p>P&#x159;ev&#xE1;d&#x11B;n&#xED; na &#x10D;&#xED;sla:\n<code>__complex__</code>, <code>__float__</code>, <code>__index__</code>, <code>__round__</code>, <code>__floor__</code>, <code>__ceil__</code>.</p>\n<p>P&#x159;ev&#xE1;d&#x11B;n&#xED; na <code>bool</code> (nap&#x159;. i v <code>if</code>):\n<code>__bool__</code>.</p>\n<p>Vytv&#xE1;&#x159;en&#xED; a ru&#x161;en&#xED; objekt&#x16F;:\n<code>__new__</code> (konstruktor &#x2013; <em>vytvo&#x159;&#xED;</em> objekt dan&#xE9; t&#x159;&#xED;dy), <code>__init__</code> (<em>inicializuje</em> objekt dan&#xE9; t&#x159;&#xED;dy), <code>__del__</code> (zavol&#xE1;no p&#x159;ed <em>zru&#x161;en&#xED;m</em> objektu).</p>\n<p>P&#x159;edefinov&#xE1;n&#xED; p&#x159;&#xED;stupu k atribut&#x16F;m:\n<code>__getattr__</code> (zavol&#xE1; se, pokud se atribut nenajde), <code>__getattribute__</code> (zavol&#xE1; se pro <em>ka&#x17E;d&#xFD;</em> p&#x159;&#xED;stup k atributu), <code>__setattr__</code>, <code>__delattr__</code>, <code>__dir__</code>.</p>\n<p>Implementace <em>context manageru</em> (pro <code>with</code>):\n<code>__enter__</code>, <code>__exit__</code>.</p>\n<p>Implementace deskriptoru (viz n&#xED;&#x17E;e):\n<code>__get__</code>, <code>__set__</code>, <code>__delete__</code>.</p>\n<p>Implementace asynchronn&#xED; funkcionality:\n<code>__await__</code>, <code>__aiter__</code>, <code>__anext__</code>, <code>__aenter__</code>, <code>__aexit__</code>.</p>\n<p>P&#x159;edefinov&#xE1;n&#xED; hierarchie d&#x11B;di&#x10D;nosti:\n<code>__instancecheck__</code>, <code>__subclasscheck__</code>.</p>\n<h2>Dekor&#xE1;tory</h2>\n<p>Dal&#x161;&#xED; v&#x11B;c, na kterou se pod&#xED;v&#xE1;me, jsou <em>dekor&#xE1;tory</em> &#x2013; zp&#x16F;sob, jak si\np&#x159;izp&#x16F;sobovat funkce.</p>\n<p>Nejjednodu&#x161;&#x161;&#xED; pou&#x17E;it&#xED; dekor&#xE1;tor&#x16F; je <em>registrace</em>:\nk funkci p&#x159;id&#xE1;me dekor&#xE1;tor a funkce se n&#x11B;kam zaregistruje, ulo&#x17E;&#xED;,\naby se dala zavolat pozd&#x11B;ji.\nTypick&#xFD; p&#x159;&#xED;klad je <code>@app.route</code> ve Flasku.</p>\n<p>My si pro p&#x159;&#xED;klad budeme cht&#xED;t ud&#x11B;lat dekor&#xE1;tor pro kalkula&#x10D;ku,\n<code>@register_operator</code>, aby fungoval tento k&#xF3;d:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">operators</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n\n<span class=\"nd\">@register_operator</span>\n<span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"nd\">@register_operator</span>\n<span class=\"k\">def</span> <span class=\"nf\">mul</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">*</span> <span class=\"n\">b</span>\n\n<span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">&apos;First number: &apos;</span><span class=\"p\">))</span>\n<span class=\"n\">operator_name</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">&apos;Operation: &apos;</span><span class=\"p\">)</span>\n<span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">&apos;Second number: &apos;</span><span class=\"p\">))</span>\n\n<span class=\"n\">func</span> <span class=\"o\">=</span> <span class=\"n\">operators</span><span class=\"p\">[</span><span class=\"n\">operator_name</span><span class=\"p\">]</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">))</span>\n</pre></div><p>Bez pou&#x17E;it&#xED; dekor&#xE1;tor&#x16F; by se to dalo napsat takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">register_operator</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">):</span>\n    <span class=\"n\">operators</span><span class=\"p\">[</span><span class=\"n\">func</span><span class=\"o\">.</span><span class=\"vm\">__name__</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">func</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"n\">register_operator</span><span class=\"p\">(</span><span class=\"n\">add</span><span class=\"p\">)</span>\n</pre></div><p>S pou&#x17E;it&#xED;m dekor&#xE1;toru je funkce <code>register_operator</code> t&#xE9;m&#x11B;&#x159; stejn&#xE1;,\njen pou&#x17E;ijeme speci&#xE1;ln&#xED; syntaxi se zavin&#xE1;&#x10D;em.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">register_operator</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">):</span>\n    <span class=\"n\">operators</span><span class=\"p\">[</span><span class=\"n\">func</span><span class=\"o\">.</span><span class=\"vm\">__name__</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">func</span>\n    <span class=\"k\">return</span> <span class=\"n\">func</span>\n\n<span class=\"nd\">@register_operator</span>\n<span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n</pre></div><p>Pou&#x17E;it&#xED; dekor&#xE1;toru je jen zkr&#xE1;cen&#xFD; z&#xE1;pis pro vol&#xE1;n&#xED; dekor&#xE1;toru jako\nfunkce &#x2013; posledn&#xED; t&#x159;i &#x159;&#xE1;dky p&#x159;edchoz&#xED;ho p&#x159;&#xED;kladu jsou ekvivalentn&#xED; tomuto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"n\">add</span> <span class=\"o\">=</span> <span class=\"n\">register_operator</span><span class=\"p\">(</span><span class=\"n\">add</span><span class=\"p\">)</span>\n</pre></div><p>Chov&#xE1;n&#xED; samotn&#xE9;ho <code>@</code> je tedy celkem trivi&#xE1;ln&#xED;.\nMagie (slo&#x17E;itost) spo&#x10D;&#xED;v&#xE1; v tom, &#x17E;e dekor&#xE1;tor je v&#x11B;t&#x161;inou funkce vy&#x161;&#x161;&#xED;ho &#x159;&#xE1;du:\nbere jinou funkci jako argument a taky jinou funkci vrac&#xED;.\nV p&#x159;&#xED;pad&#x11B; registrace vrac&#xED; stejnou funkci jako dostala &#x2013; ale to nen&#xED; povinn&#xE9;.</p>\n<p>&#x10C;asto se setk&#xE1;me s dekor&#xE1;tory, kter&#xE9; dekorovanou funkci n&#x11B;jak modifikuj&#xED;.\nNap&#x159;&#xED;klad m&#x16F;&#x17E;eme napsat dekor&#xE1;tor, kter&#xFD; v na&#x161;&#xED; kalkula&#x10D;ce p&#x159;evede vstup\nna re&#xE1;ln&#xE1; &#x10D;&#xED;sla.\nD&#x11B;l&#xE1; to tak, &#x17E;e definuje <em>novou funkci</em>, kter&#xE1; vol&#xE1; tu p&#x16F;vodn&#xED; &#x2013; ale p&#x159;ed nebo\npo tomto vol&#xE1;n&#xED; m&#x16F;&#x17E;e d&#x11B;lat i n&#x11B;co jin&#xE9;ho.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">to_floats</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"nf\">outer_function</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n        <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">)</span>\n        <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">b</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"n\">func</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">outer_function</span>\n\n<span class=\"nd\">@to_floats</span>\n<span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"sd\">&quot;&quot;&quot;Adds two numbers&quot;&quot;&quot;</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">add</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"s1\">&apos;2&apos;</span><span class=\"p\">))</span>\n</pre></div><p>Takto funguje v&#x11B;t&#x161;ina dekor&#xE1;tor&#x16F;, kter&#xE9; m&#x11B;n&#xED; chov&#xE1;n&#xED; dekorovan&#xE9; funkce.\nNar&#xE1;&#x17E;&#xED; s t&#xED;m ale na jeden probl&#xE9;m: nov&#x11B; nadefinovan&#xE1; funkce m&#xE1; vlastn&#xED; jm&#xE9;no\n(a dokumenta&#x10D;n&#xED; &#x159;et&#x11B;zec a podobn&#xE9; informace), co&#x17E; kaz&#xED; iluzi, &#x17E;e jsme\np&#x16F;vodn&#xED; funkci jen tro&#x161;ku zm&#x11B;nili:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">add</span><span class=\"p\">)</span>\n<span class=\"n\">help</span><span class=\"p\">(</span><span class=\"n\">add</span><span class=\"p\">)</span>\n</pre></div><p>&#x158;e&#x161;en&#xED; je jednoduch&#xE9; &#x2013; zkop&#xED;rovat jm&#xE9;no, dokumenta&#x10D;n&#xED; &#x159;et&#x11B;zec atd. z jedn&#xE9;\nfunkce na druhou.\nNa to ve standardn&#xED; knihovn&#x11B; existuje dekor&#xE1;tor jm&#xE9;nem <code>functools.wraps</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">functools</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">to_floats</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">):</span>\n    <span class=\"nd\">@functools.wraps</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">)</span>\n    <span class=\"k\">def</span> <span class=\"nf\">outer_function</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n        <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">)</span>\n        <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">b</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"n\">func</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">outer_function</span>\n</pre></div><p>S <code>wraps</code> bude <code>help(add)</code> fungovat spr&#xE1;vn&#x11B; &#x2013; uk&#xE1;&#x17E;e p&#x16F;vodn&#xED; jm&#xE9;no\na dokumenta&#x10D;n&#xED; &#x159;et&#x11B;zec.</p>\n<p>Z vol&#xE1;n&#xED; <code>wraps(func)</code> je vid&#x11B;t, &#x17E;e jako dekor&#xE1;tor m&#x16F;&#x17E;eme pou&#x17E;&#xED;t i vol&#xE1;n&#xED;\nfunkce, ne jen funkci samotnou.\nBudeme-li cht&#xED;t napsat dekor&#xE1;tor, kter&#xFD; tohle um&#xED;, pot&#x159;ebujeme napsat\nfunkci je&#x161;t&#x11B; vy&#x161;&#x161;&#xED;ho &#x159;&#xE1;du &#x2013; toti&#x17E; funkci, kter&#xE1; po zavol&#xE1;n&#xED; vr&#xE1;t&#xED; dekor&#xE1;tor:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">operators</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">register_operator</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"nf\">decorator</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">):</span>\n        <span class=\"n\">operators</span><span class=\"p\">[</span><span class=\"n\">name</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">func</span>\n        <span class=\"k\">return</span> <span class=\"n\">func</span>\n    <span class=\"k\">return</span> <span class=\"n\">decorator</span>\n\n<span class=\"nd\">@register_operator</span><span class=\"p\">(</span><span class=\"s1\">&apos;+&apos;</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"nd\">@register_operator</span><span class=\"p\">(</span><span class=\"s1\">&apos;*&apos;</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">mul</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">*</span> <span class=\"n\">b</span>\n\n<span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">&apos;First number: &apos;</span><span class=\"p\">))</span>\n<span class=\"n\">operator_name</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">&apos;Operation: &apos;</span><span class=\"p\">)</span>\n<span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">&apos;Second number: &apos;</span><span class=\"p\">))</span>\n\n<span class=\"n\">func</span> <span class=\"o\">=</span> <span class=\"n\">operators</span><span class=\"p\">[</span><span class=\"n\">operator_name</span><span class=\"p\">]</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">))</span>\n</pre></div><p>&#x158;&#xE1;dek <code>@register_operator(&apos;+&apos;)</code> d&#x11B;l&#xE1; (jak u&#x17E; v&#xED;me) to stejn&#xE9;, jako bychom hned\nza funkc&#xED; napsali <code>add = register_operator(&apos;+&apos;)(add)</code>.</p>\n<p>Budete-li cht&#xED;t napsat dekor&#xE1;tor, kter&#xFD; bere argumenty, a p&#x159;itom je&#x161;t&#x11B;\n&#x201E;m&#x11B;n&#xED;&#x201C; dekorovanou funkci, dostanete se na t&#x159;i funkce zano&#x159;en&#xE9; v sob&#x11B;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">functools</span>\n<span class=\"n\">operators</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">register_operator</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"nf\">to_floats</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">):</span>\n\n        <span class=\"nd\">@functools.wraps</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">)</span>\n        <span class=\"k\">def</span> <span class=\"nf\">outer_function</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n            <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">)</span>\n            <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">b</span><span class=\"p\">)</span>\n            <span class=\"k\">return</span> <span class=\"n\">func</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">)</span>\n\n        <span class=\"n\">operators</span><span class=\"p\">[</span><span class=\"n\">name</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">outer_function</span>\n        <span class=\"k\">return</span> <span class=\"n\">outer_function</span>\n\n    <span class=\"k\">return</span> <span class=\"n\">to_floats</span>\n\n<span class=\"nd\">@register_operator</span><span class=\"p\">(</span><span class=\"s1\">&apos;+&apos;</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"n\">func</span> <span class=\"o\">=</span> <span class=\"n\">operators</span><span class=\"p\">[</span><span class=\"s1\">&apos;+&apos;</span><span class=\"p\">]</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">func</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"s1\">&apos;2&apos;</span><span class=\"p\">))</span>\n</pre></div><p>Dekor&#xE1;tor&#x16F; se na jedn&#xE9; funkci d&#xE1; pou&#x17E;&#xED;t v&#xED;c:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"nd\">@register_operator</span><span class=\"p\">(</span><span class=\"s1\">&apos;&#xD7;&apos;</span><span class=\"p\">)</span>\n<span class=\"nd\">@register_operator</span><span class=\"p\">(</span><span class=\"s1\">&apos;*&apos;</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">mul</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">*</span> <span class=\"n\">b</span>\n</pre></div><p>&#xDA;pln&#x11B; stejn&#x11B; jako funkce se daj&#xED; dekorovat i t&#x159;&#xED;dy.\nDekor&#xE1;tor dostane t&#x159;&#xED;du jako prvn&#xED; argument a t&#x159;&#xED;da se nahrad&#xED; t&#xED;m,\nco dekor&#xE1;tor vr&#xE1;t&#xED;.</p>\n<h2>Deskriptory</h2>\n<p>Jeden z nejmagi&#x10D;t&#x11B;j&#x161;&#xED;ch oper&#xE1;tor&#x16F; v Pythonu je <code>.</code>, te&#x10D;ka.\nJe magick&#xFD; v obou v&#xFD;znamech &#x2013; v&#x11B;t&#x161;ina lid&#xED; ho pou&#x17E;&#xED;v&#xE1;, a&#x10D;koli nem&#xE1; tu&#x161;en&#xED;, co p&#x159;esn&#x11B; d&#x11B;l&#xE1;, a d&#xE1; se p&#x159;edefinovat tolika r&#x16F;zn&#xFD;mi zp&#x16F;soby, &#x17E;e to vyd&#xE1; na <a href=\"https://www.youtube.com/watch?v=NiSqG6s8skA\">celou p&#x159;edn&#xE1;&#x161;ku</a>.</p>\n<p>Pomoc&#xED; te&#x10D;ky zapisujeme t&#x159;i operace: &#x10D;ten&#xED; atributu (<code>print(foo.bar)</code>), zapisov&#xE1;n&#xED; (<code>foo.bar = 3</code>) a maz&#xE1;n&#xED; (<code>del foo.bar</code>).\nTady se zam&#x11B;&#x159;&#xED;me hlavn&#x11B; na nejmagi&#x10D;t&#x11B;j&#x161;&#xED; z nich, &#x10D;ten&#xED;.</p>\n<p>Kdykoli atribut &#x10D;teme pomoc&#xED; te&#x10D;ky, hled&#xE1; se n&#x11B;kolika m&#xED;stech:</p>\n<ul>\n<li>na samotn&#xE9; instanci objektu,</li>\n<li>pokud se tam nenajde, tak na t&#x159;&#xED;d&#x11B;,</li>\n<li>pokud se nenajde ani tam, tak na rodi&#x10D;ovsk&#xFD;ch t&#x159;&#xED;d&#xE1;ch (v p&#x159;&#xED;pad&#x11B; v&#xED;cen&#xE1;sobn&#xE9; d&#x11B;di&#x10D;nosti podle <a href=\"https://www.python.org/download/releases/2.3/mro/\">MRO</a>),</li>\n<li>a pokud st&#xE1;le nen&#xED; k nalezen&#xED;, vyhod&#xED; se <code>AttributeError</code>.</li>\n</ul>\n<p>To je trochu zjednodu&#x161;en&#xFD;, ale u&#x17E;ite&#x10D;n&#xFD; model.</p>\n<p>Speci&#xE1;ln&#xED; metody, kter&#xE9; se nevolaj&#xED; pomoc&#xED; te&#x10D;ky, p&#x159;eskakuj&#xED; prvn&#xED; krok: metoda <code>__add__</code> tedy mus&#xED; b&#xFD;t definov&#xE1;na na <em>t&#x159;&#xED;d&#x11B;</em>, aby se zavolala pro <code>a + b</code>.</p>\n<blockquote><p><em>(Pozn&#xE1;mka nav&#xED;c pro ty, kdo &#x10D;tou tento text podruh&#xE9;: na metat&#x159;&#xED;d&#x11B; se atribut nehled&#xE1;; nap&#x159;. existuje-li <code>type.mro</code>, najde se <code>str.mro</code>, ale u&#x17E; ne <code>&quot;&quot;.mro</code>)</em></p>\n</blockquote>\n<p>Pod&#xED;vejme se te&#x10F; na z&#xED;sk&#xE1;v&#xE1;n&#xED; atributu tro&#x161;ku podrobn&#x11B;ji. Je to pom&#x11B;rn&#x11B; komplikovan&#xFD; proces a existuje n&#x11B;kolik zp&#x16F;sob&#x16F;, jak ho p&#x159;izp&#x16F;sobit. Nejjednodu&#x161;&#x161;&#xED; je dvojice speci&#xE1;ln&#xED;ch metod:</p>\n<ul>\n<li><code>__getattribute__</code>, kter&#xE1; <em>kompletn&#x11B; p&#x159;edefinuje</em> funkci <code>.</code> pro &#x10D;ten&#xED; atributu, a</li>\n<li><code>__getattr__</code>, kter&#xE1; se zavol&#xE1;, a&#x17E; kdy&#x17E; se atribut nenajde norm&#xE1;ln&#xED;m zp&#x16F;sobem.</li>\n</ul>\n<p>Prvn&#xED; z nich nedoporu&#x10D;uji pou&#x17E;&#xED;vat, proto&#x17E;e je <em>p&#x159;&#xED;li&#x161;</em> obecn&#xE1; (pokusy se z n&#xED; dostat ke stavu objektu kon&#x10D;&#xED;vaj&#xED; nekone&#x10D;nou rekurz&#xED;).\nP&#x159;&#xED;klad druh&#xE9;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Palette</span><span class=\"p\">:</span>\n    <span class=\"n\">red</span> <span class=\"o\">=</span> <span class=\"mi\">255</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n    <span class=\"n\">green</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">255</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__getattr__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">attr_name</span><span class=\"p\">):</span>\n        <span class=\"n\">prefix</span><span class=\"p\">,</span> <span class=\"n\">sep</span><span class=\"p\">,</span> <span class=\"n\">suffix</span> <span class=\"o\">=</span> <span class=\"n\">attr_name</span><span class=\"o\">.</span><span class=\"n\">partition</span><span class=\"p\">(</span><span class=\"s1\">&apos;_&apos;</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"n\">prefix</span> <span class=\"o\">==</span> <span class=\"s1\">&apos;dark&apos;</span><span class=\"p\">:</span>\n            <span class=\"n\">original_color</span> <span class=\"o\">=</span> <span class=\"nb\">getattr</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">suffix</span><span class=\"p\">)</span>\n            <span class=\"k\">return</span> <span class=\"nb\">tuple</span><span class=\"p\">(</span><span class=\"n\">c</span><span class=\"o\">//</span><span class=\"mi\">2</span> <span class=\"k\">for</span> <span class=\"n\">c</span> <span class=\"ow\">in</span> <span class=\"n\">original_color</span><span class=\"p\">)</span>\n        <span class=\"k\">else</span><span class=\"p\">:</span>\n            <span class=\"k\">raise</span> <span class=\"ne\">AttributeError</span><span class=\"p\">(</span><span class=\"n\">attr_name</span><span class=\"p\">)</span>\n\n<span class=\"n\">palette</span> <span class=\"o\">=</span> <span class=\"n\">Palette</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">palette</span><span class=\"o\">.</span><span class=\"n\">dark_red</span><span class=\"p\">)</span>\n</pre></div><p>(P&#x159;edpokl&#xE1;d&#xE1;m &#x17E;e zn&#xE1;te funkci <code>getattr</code>; kdyby ne: <code>getattr(foo, &quot;bar&quot;)</code> d&#x11B;l&#xE1; tot&#xE9;&#x17E; co <code>foo.bar</code> &#x2013; jen je jm&#xE9;no atributu p&#x159;ed&#xE1;no jako &#x159;et&#x11B;zec, tak&#x17E;e m&#x16F;&#x17E;e b&#xFD;t nap&#x159;. v prom&#x11B;nn&#xE9;. Podobn&#x11B; existuj&#xED; <code>setattr(instance, attr_name, new_value)</code> a <code>delattr(setattr(instance, attr_name)</code>.)</p>\n<p>Metoda <code>__getattr__</code> je v&#x11B;t&#x161;inou tak trochu kan&#xF3;n na vrabce: ve v&#x11B;t&#x161;in&#x11B; p&#x159;&#xED;pad&#x16F; nepot&#x159;ebujeme nastavit chov&#xE1;n&#xED; <em>v&#x161;ech</em> neexistuj&#xED;c&#xED;ch atribut&#x16F;, ale jenom jednoho nebo n&#x11B;kolika konkr&#xE9;tn&#xED;ch.\nNap&#x159;&#xED;klad m&#xE1;me t&#x159;&#xED;du pro 2D bod s atributy <code>x</code> a <code>y</code> a pot&#x159;ebujeme i atribut pro dvojici <code>(x, y)</code>.\nToto se &#x10D;asto d&#x11B;l&#xE1; pomoc&#xED; dekor&#xE1;toru <code>property</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Point</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">x</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">y</span>\n\n    <span class=\"nd\">@property</span>\n    <span class=\"k\">def</span> <span class=\"nf\">pos</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">y</span>\n\n<span class=\"n\">point</span> <span class=\"o\">=</span> <span class=\"n\">Point</span><span class=\"p\">(</span><span class=\"mi\">41</span><span class=\"p\">,</span> <span class=\"mi\">8</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">point</span><span class=\"o\">.</span><span class=\"n\">pos</span><span class=\"p\">)</span>\n</pre></div><p>Jak to ale funguje? Dekor&#xE1;tor <code>property</code> je t&#x159;&#xED;da, jakou m&#x16F;&#x17E;ete teoreticky napsat sami v Pythonu.\nJe to <em>deskriptor</em>, objekt, kter&#xFD; v r&#xE1;mci n&#x11B;jak&#xE9; t&#x159;&#xED;dy <em>popisuje</em> jak p&#x159;istupovat k n&#x11B;jak&#xE9;mu atributu.</p>\n<p>Nejl&#xE9;pe se deskriptory vysv&#x11B;tl&#xED; na p&#x159;&#xED;kladu:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"c1\"># (Omluvte pros&#xED;m &#x10D;e&#x161;tinu v k&#xF3;du)</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Descriptor2D</span><span class=\"p\">:</span>\n    <span class=\"sd\">&quot;&quot;&quot;Popisuje atribut, kter&#xFD; kombinuje dva jin&#xE9; atributy do dvojice&quot;&quot;&quot;</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">name1</span><span class=\"p\">,</span> <span class=\"n\">name2</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name1</span> <span class=\"o\">=</span> <span class=\"n\">name1</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name2</span> <span class=\"o\">=</span> <span class=\"n\">name2</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__get__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">cls</span><span class=\"o\">=</span><span class=\"bp\">None</span><span class=\"p\">):</span>\n        <span class=\"sd\">&quot;&quot;&quot;Vol&#xE1; se, kdy&#x17E; je t&#x159;eba na&#x10D;&#xED;st atribut dan&#xE9; `instance` na dan&#xE9; t&#x159;&#xED;d&#x11B; `cls`.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n\n        <span class=\"k\">if</span> <span class=\"n\">instance</span> <span class=\"ow\">is</span> <span class=\"ow\">not</span> <span class=\"bp\">None</span><span class=\"p\">:</span>\n            <span class=\"c1\"># Je-li instance nastavena, &#x10D;teme atribut z n&#xED;.</span>\n            <span class=\"k\">return</span> <span class=\"nb\">getattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name1</span><span class=\"p\">),</span> <span class=\"nb\">getattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name2</span><span class=\"p\">)</span>\n        <span class=\"k\">else</span><span class=\"p\">:</span>\n            <span class=\"c1\"># Je-li instance None, &#x10D;teme atribut p&#x159;&#xED;mo ze t&#x159;&#xED;dy `cls`;</span>\n            <span class=\"c1\"># v tomto p&#x159;&#xED;pad&#x11B; slu&#x161;n&#xE9; deskriptory v&#x11B;t&#x161;inou vrac&#xED; deskriptor samotn&#xFD;.</span>\n            <span class=\"k\">return</span> <span class=\"bp\">self</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Rect</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">,</span> <span class=\"n\">w</span><span class=\"p\">,</span> <span class=\"n\">h</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">x</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">y</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">w</span> <span class=\"o\">=</span> <span class=\"n\">w</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">h</span> <span class=\"o\">=</span> <span class=\"n\">h</span>\n\n    <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">Descriptor2D</span><span class=\"p\">(</span><span class=\"s1\">&apos;x&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;y&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">Descriptor2D</span><span class=\"p\">(</span><span class=\"s1\">&apos;w&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;h&apos;</span><span class=\"p\">)</span>\n\n<span class=\"n\">rect</span> <span class=\"o\">=</span> <span class=\"n\">Rect</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">rect</span><span class=\"o\">.</span><span class=\"n\">pos</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">rect</span><span class=\"o\">.</span><span class=\"n\">size</span><span class=\"p\">)</span>\n\n<span class=\"c1\"># &#x10C;ten&#xED; atributu p&#x159;&#xED;mo ze t&#x159;&#xED;dy:</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">Rect</span><span class=\"o\">.</span><span class=\"n\">pos</span><span class=\"p\">)</span>\n</pre></div><p>Deskriptory jsou tedy sou&#x10D;&#xE1;st t&#x159;&#xED;dy &#x2013; atributy s n&#x11B;jak&#xFD;m jm&#xE9;nem. Popisuj&#xED;, jak se bude p&#x159;istupovat k atributu dan&#xE9;ho jm&#xE9;na.</p>\n<p>Existuj&#xED; dva druhy deskriptor&#x16F;: <em>data descriptor</em> a <em>non-data descriptor</em>.\nLi&#x161;&#xED; se v tom, jestli popisuj&#xED; jen, jak se dan&#xFD; atribut <em>&#x10D;te</em>, nebo i jak se do n&#x11B;j <em>zapisuje</em>.\nV&#xFD;&#x161;e uveden&#xFD; deskriptor je <em>non-data</em>: ovl&#xE1;d&#xE1; jen &#x10D;ten&#xED;. Z&#xE1;pis funguje jako u norm&#xE1;ln&#xED;ch atribut&#x16F;:\np&#x159;ep&#xED;&#x161;e aktu&#xE1;ln&#xED; hodnotu &#x2013; a nov&#xE1; hodnota se pak pou&#x17E;ije m&#xED;sto vol&#xE1;n&#xED; deskriptoru:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">rect</span><span class=\"o\">.</span><span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"s1\">&apos;haha&apos;</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">rect</span><span class=\"o\">.</span><span class=\"n\">pos</span><span class=\"p\">)</span>\n</pre></div><p>Abychom tomu zabr&#xE1;nili, m&#x16F;&#x17E;eme na deskriptoru nadefinovat speci&#xE1;ln&#xED; metodu <code>__set__</code> (nebo <code>__delete__</code>), kter&#xE1; popisuje,\njak se atribut nastavuje (resp. ma&#x17E;e).\nT&#xED;m vznikne <em>data descriptor</em>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Descriptor2D</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">name1</span><span class=\"p\">,</span> <span class=\"n\">name2</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name1</span> <span class=\"o\">=</span> <span class=\"n\">name1</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name2</span> <span class=\"o\">=</span> <span class=\"n\">name2</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__get__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">cls</span><span class=\"o\">=</span><span class=\"bp\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"n\">instance</span> <span class=\"ow\">is</span> <span class=\"ow\">not</span> <span class=\"bp\">None</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"nb\">getattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name1</span><span class=\"p\">),</span> <span class=\"nb\">getattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name2</span><span class=\"p\">)</span>\n        <span class=\"k\">else</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"bp\">self</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__set__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"n\">new_value</span><span class=\"p\">):</span>\n        <span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">new_value</span>\n        <span class=\"nb\">setattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name1</span><span class=\"p\">,</span> <span class=\"n\">a</span><span class=\"p\">)</span>\n        <span class=\"nb\">setattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name2</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__delete__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">instance</span><span class=\"p\">):</span>\n        <span class=\"nb\">delattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name1</span><span class=\"p\">)</span>\n        <span class=\"nb\">delattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">name2</span><span class=\"p\">)</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Rect</span><span class=\"p\">:</span>\n    <span class=\"c1\"># jako p&#x159;edt&#xED;m</span>\n\n<span class=\"n\">rect</span> <span class=\"o\">=</span> <span class=\"n\">Rect</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">)</span>\n<span class=\"n\">rect</span><span class=\"o\">.</span><span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"mi\">123</span><span class=\"p\">,</span> <span class=\"mi\">456</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">rect</span><span class=\"o\">.</span><span class=\"n\">pos</span><span class=\"p\">)</span>\n</pre></div><p>U&#x17E; zm&#xED;n&#x11B;n&#xFD; vestav&#x11B;n&#xFD; deskriptor <code>property</code> je <em>data descriptor</em>.\nPopisuje jak &#x10D;ten&#xED;, tak z&#xE1;pis atributu. Pokud mu nenastav&#xED;me funkci pro z&#xE1;pis, vyhod&#xED; ze sv&#xE9; metody  <code>__set__</code> v&#xFD;jimku <code>AttributeError</code> se zpr&#xE1;vou, &#x17E;e do atributu se zapisovat ned&#xE1;. (To je trochu magick&#xE1; odchylka od norm&#xE1;ln&#xED;ho chov&#xE1;n&#xED; Pythonu, kdy atributy zapisovat jdou.)</p>\n<p>Nej&#x10D;ast&#x11B;j&#x161;&#xED; p&#x159;&#xED;klad <em>non-data</em> deskriptoru je oby&#x10D;ejn&#xE1; funkce.\nKa&#x17E;d&#xE1; funkce toti&#x17E; funguje jako deskriptor: m&#xE1; speci&#xE1;ln&#xED; metodu <code>__get__</code>, kter&#xE1; zaji&#x161;&#x165;uje, &#x17E;e pokud je nastavena na t&#x159;&#xED;d&#x11B;, dan&#xFD;m atributem nedostaneme <em>funkci</em>, ale <em>metodu</em> (s &#x201E;p&#x159;edvypln&#x11B;n&#xFD;m&#x201C; parametrem <code>self</code>).</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">foo</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"mi\">4</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">C</span><span class=\"p\">:</span>\n    <span class=\"n\">foo</span> <span class=\"o\">=</span> <span class=\"n\">foo</span>\n\n<span class=\"n\">c</span> <span class=\"o\">=</span> <span class=\"n\">C</span><span class=\"p\">()</span>\n\n<span class=\"c1\"># Oby&#x10D;ejn&#xE1; funkce</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">C</span><span class=\"o\">.</span><span class=\"n\">foo</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">foo</span><span class=\"p\">)</span>\n\n<span class=\"c1\"># Metoda</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">C</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">foo</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">foo</span><span class=\"o\">.</span><span class=\"fm\">__get__</span><span class=\"p\">(</span><span class=\"n\">c</span><span class=\"p\">))</span>\n</pre></div><p>Proto&#x17E;e je to <em>non-data</em> deskriptor, m&#x16F;&#x17E;eme v jednotliv&#xFD;ch instanc&#xED;ch t&#x159;&#xED;dy\ndan&#xFD; atribut p&#x159;epsat n&#x11B;&#x10D;&#xED;m jin&#xFD;m, &#x10D;&#xED;m&#x17E; metodu znep&#x159;&#xED;stupn&#xED;me.</p>\n<p>Jako zaj&#xED;mavost uvedu <em>non-data</em> deskriptor, kter&#xFD; p&#x159;episuje sv&#x16F;j vlastn&#xED; atribut.\nFunguje podobn&#x11B; jako <code>@property</code>, jen se v&#xFD;sledek vypo&#x10D;&#xED;t&#xE1; pouze jednou a ulo&#x17E;&#xED; se jako norm&#xE1;ln&#xED; atribut.\nP&#x159;i dal&#x161;&#xED;m p&#x159;&#xED;stupu k atributu u&#x17E; se pou&#x17E;ije ulo&#x17E;en&#xE1; hodnota.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">reify</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">func</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">func</span> <span class=\"o\">=</span> <span class=\"n\">func</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__get__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">cls</span><span class=\"o\">=</span><span class=\"bp\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"n\">instance</span> <span class=\"ow\">is</span> <span class=\"bp\">None</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"bp\">self</span>\n        <span class=\"n\">val</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">func</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">)</span>\n        <span class=\"nb\">setattr</span><span class=\"p\">(</span><span class=\"n\">instance</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">func</span><span class=\"o\">.</span><span class=\"vm\">__name__</span><span class=\"p\">,</span> <span class=\"n\">val</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"n\">val</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Vector</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">x</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">y</span>\n\n    <span class=\"nd\">@reify</span>\n    <span class=\"k\">def</span> <span class=\"nf\">length</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Running expensive computation...&apos;</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">**</span> <span class=\"mi\">2</span> <span class=\"o\">+</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">y</span> <span class=\"o\">**</span> <span class=\"mi\">2</span><span class=\"p\">)</span> <span class=\"o\">**</span> <span class=\"mf\">0.5</span>\n\n<span class=\"n\">vect</span> <span class=\"o\">=</span> <span class=\"n\">Vector</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">vect</span><span class=\"o\">.</span><span class=\"n\">length</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">vect</span><span class=\"o\">.</span><span class=\"n\">length</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">vect</span><span class=\"o\">.</span><span class=\"n\">length</span><span class=\"p\">)</span>\n</pre></div><p>Kompletn&#xED; implementace je nap&#x159;. ve frameworku Pyramid jako <a href=\"http://docs.pylonsproject.org/projects/pyramid/en/latest/_modules/pyramid/decorator.html\">pyramid.decorator.reify</a>.</p>\n<h2>Konstruktor</h2>\n<p>T&#x159;&#xED;dy v Pythonu m&#x16F;&#x17E;ou m&#xED;t <em>konstruktor</em> &#x2013; funkci, kter&#xE1; se zavol&#xE1;, aby\nvytvo&#x159;ila objekt dan&#xE9;ho typu.\nToto nen&#xED; zn&#xE1;m&#xE1; metoda <code>__init__</code> &#x2013; ta objekt nevytv&#xE1;&#x159;&#xED;, ta dostane u&#x17E;\np&#x159;edp&#x159;ipraven&#xFD; <code>self</code>, kter&#xFD; jen napln&#xED; atributy.\nOpravdov&#xFD; konstruktor se jmenuje <code>__new__</code> a chov&#xE1; se jako <code>classmethod</code>:\nm&#xED;sto <code>self</code> bere t&#x159;&#xED;du, jej&#xED;&#x17E; instanci m&#xE1; vytvo&#x159;it.</p>\n<p>Opravdov&#xFD; konstruktor se &#x201E;hod&#xED;&#x201C; pro vytv&#xE1;&#x159;en&#xED; <em>singleton&#x16F;</em>, t&#x159;&#xED;d, kter&#xE9; maj&#xED; jen\njednu instanci:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Singleton</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__new__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">):</span>\n        <span class=\"k\">try</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"bp\">cls</span><span class=\"o\">.</span><span class=\"n\">_instance</span>\n        <span class=\"k\">except</span> <span class=\"ne\">AttributeError</span><span class=\"p\">:</span>\n            <span class=\"bp\">cls</span><span class=\"o\">.</span><span class=\"n\">_instance</span> <span class=\"o\">=</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__new__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">)</span>\n            <span class=\"k\">return</span> <span class=\"bp\">cls</span><span class=\"o\">.</span><span class=\"n\">_instance</span>\n\n<span class=\"k\">assert</span> <span class=\"n\">Singleton</span><span class=\"p\">()</span> <span class=\"ow\">is</span> <span class=\"n\">Singleton</span><span class=\"p\">()</span>\n</pre></div><p>Podobn&#xFD; trik lze pou&#x17E;&#xED;t pro t&#x159;&#xED;du podobnou <code>bool</code>, kter&#xE1; m&#xE1; pouze dv&#x11B; instance:\n<code>bool(1) is bool(2)</code>.</p>\n<p>Metoda <code>__new__</code> se hod&#xED;, kdy&#x17E; chceme d&#x11B;dit z nem&#x11B;niteln&#xE9; (<em>immutable</em>)\nt&#x159;&#xED;dy jako <code>tuple</code>.\nMetoda <code>__init__</code> sice dostane <code>self</code>, ale cokoli z nadt&#x159;&#xED;dy u&#x17E; nem&#x16F;&#x17E;e m&#x11B;nit.\nJe ale mo&#x17E;n&#xE9; p&#x159;edefinovat <code>__new__</code>.</p>\n<p>Norm&#xE1;ln&#x11B; bere <code>tuple</code> jedin&#xFD; argument, <code>tuple([1, 2])</code>.\nChceme-li br&#xE1;t dva, d&#xE1; se to ud&#x11B;lat takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Point</span><span class=\"p\">(</span><span class=\"nb\">tuple</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__new__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__new__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">))</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">Point</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">))</span>\n</pre></div><h2>Metat&#x159;&#xED;dy</h2>\n<p>Posledn&#xED; v&#x11B;c, na kterou se pod&#xED;v&#xE1;me, jsou metat&#x159;&#xED;dy.</p>\n<p>Za&#x10D;neme zlehka: pokud p&#x159;i definici t&#x159;&#xED;dy zad&#xE1;me n&#x11B;jakou funkci jako pojmenovan&#xFD;\nparametr <code>metaclass</code>, funkce se zavol&#xE1; s informacemi pot&#x159;ebn&#xFD;mi pro vytvo&#x159;en&#xED;\nt&#x159;&#xED;dy.\nTy m&#x16F;&#x17E;eme pou&#x17E;&#xED;t, nebo &#xFA;pln&#x11B; ignorovat a vr&#xE1;tit n&#x11B;co jin&#xE9;ho:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">fake_metaclass</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">bases</span><span class=\"p\">,</span> <span class=\"n\">namespace</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"mi\">42</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">NotAClass</span><span class=\"p\">(</span><span class=\"n\">metaclass</span><span class=\"o\">=</span><span class=\"n\">fake_metaclass</span><span class=\"p\">):</span>\n    <span class=\"k\">pass</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">NotAClass</span><span class=\"p\">)</span>\n</pre></div><p>Argumenty, kter&#xE9; &#x201E;metat&#x159;&#xED;da&#x201D; dostane, jsou t&#x159;i: jm&#xE9;no t&#x159;&#xED;dy, <em>n</em>-tice\nnadt&#x159;&#xED;d a jmenn&#xFD; prostor &#x2013; slovn&#xED;k s prom&#x11B;nn&#xFD;mi, kter&#xE9; vznikly vykon&#xE1;n&#xED;m\nt&#x11B;la p&#x159;&#xED;kazu <code>class</code>.\n(Ve jmenn&#xE9;m prostoru jsou implicitn&#x11B; nastaven&#xE9; z&#xE1;znamy <code>__module__</code>\na <code>__qualname__</code>, kter&#xE9; p&#x159;id&#xE1;v&#xE1; samotn&#xFD; p&#x159;&#xED;kaz <code>class</code>.)</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">fake_metaclass</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">bases</span><span class=\"p\">,</span> <span class=\"n\">namespace</span><span class=\"p\">):</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;name:&apos;</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;bases:&apos;</span><span class=\"p\">,</span> <span class=\"n\">bases</span><span class=\"p\">)</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;namespace:&apos;</span><span class=\"p\">,</span> <span class=\"n\">namespace</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"mi\">42</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">NotAClass</span><span class=\"p\">(</span><span class=\"nb\">int</span><span class=\"p\">,</span> <span class=\"n\">metaclass</span><span class=\"o\">=</span><span class=\"n\">fake_metaclass</span><span class=\"p\">):</span>\n    <span class=\"n\">foo</span> <span class=\"o\">=</span> <span class=\"mi\">123</span>\n    <span class=\"k\">def</span> <span class=\"nf\">inc</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"bp\">self</span> <span class=\"o\">+</span> <span class=\"mi\">1</span>\n</pre></div><p>Kdy&#x17E; <code>metaclass</code> nezad&#xE1;me, pou&#x17E;ije se v&#xFD;choz&#xED; <em>metat&#x159;&#xED;da</em>, tedy t&#x159;&#xED;da t&#x159;&#xED;dy.\nV Pythonu je to <code>type</code>.\nPokud ji zavol&#xE1;me s vhodn&#xFD;mi argumenty, dostaneme norm&#xE1;ln&#xED; t&#x159;&#xED;du:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">MyInt</span> <span class=\"o\">=</span> <span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"s1\">&apos;MyInt&apos;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"nb\">int</span><span class=\"p\">,</span> <span class=\"p\">),</span> <span class=\"p\">{</span><span class=\"s1\">&apos;foo&apos;</span><span class=\"p\">:</span> <span class=\"mi\">123</span><span class=\"p\">,</span> <span class=\"s1\">&apos;inc&apos;</span><span class=\"p\">:</span> <span class=\"k\">lambda</span> <span class=\"bp\">self</span><span class=\"p\">:</span> <span class=\"bp\">self</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">})</span>\n\n<span class=\"n\">three</span> <span class=\"o\">=</span> <span class=\"n\">MyInt</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">three</span><span class=\"o\">.</span><span class=\"n\">inc</span><span class=\"p\">())</span>\n</pre></div><p>Krom&#x11B; toho se <code>type</code> d&#xE1; zavolat i s jedn&#xED;m argumentem; v tom p&#x159;&#xED;pad&#x11B; vr&#xE1;t&#xED;\ntyp (t&#x159;&#xED;du) dan&#xE9;ho argumentu.\n(Tohle chov&#xE1;n&#xED; &#x2013; funkce, kter&#xE1; d&#x11B;l&#xE1; &#xFA;pln&#x11B; r&#x16F;zn&#xE9; v&#x11B;ci v z&#xE1;vislosti na po&#x10D;tu\nargument&#x16F; &#x2013; v Pythonu &#x10D;asto nevid&#xED;me.\nJe to ne&#x161;&#x165;astn&#xE1; v&#xFD;jimka, kter&#xE1; p&#x159;e&#x17E;&#xED;v&#xE1; z historick&#xFD;ch d&#x16F;vod&#x16F;.)</p>\n<p>Poj&#x10F;me se pod&#xED;vat na t&#x159;&#xED;dy n&#x11B;kolika z&#xE1;kladn&#xED;ch objekt&#x16F;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"c1\"># T&#x159;&#xED;da z&#xE1;kladn&#xED;ch objekt&#x16F;</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">))</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"s2\">&quot;abc&quot;</span><span class=\"p\">))</span>\n\n<span class=\"c1\"># T&#x159;&#xED;da t&#x159;&#xED;dy &#x2013; metat&#x159;&#xED;da.</span>\n<span class=\"c1\"># T&#x159;&#xED;da v&#x11B;t&#x161;iny t&#x159;&#xED;d v Pythonu je `type`</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"nb\">int</span><span class=\"p\">))</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)))</span>\n\n<span class=\"c1\"># T&#x159;&#xED;da t&#x159;&#xED;dy t&#x159;&#xED;dy</span>\n<span class=\"c1\"># Samotn&#xE1; `type` je jedna z t&#xE9; v&#x11B;t&#x161;iny t&#x159;&#xED;d; jej&#xED; t&#x159;&#xED;da je `type`</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">))</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">))))</span>\n</pre></div><p>Objekty t&#x159;&#xED;dy <code>type</code> (tedy t&#x159;&#xED;dy) se norm&#xE1;ln&#x11B; tvo&#x159;&#xED; p&#x159;&#xED;kazem <code>class</code>.\nExplicitn&#x11B; to m&#x16F;&#x17E;eme napsat takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">NormalClass</span><span class=\"p\">(</span><span class=\"n\">metaclass</span><span class=\"o\">=</span><span class=\"nb\">type</span><span class=\"p\">):</span>\n    <span class=\"n\">foo</span> <span class=\"o\">=</span> <span class=\"mi\">123</span>\n</pre></div><p>Kdy&#x17E; budeme cht&#xED;t chov&#xE1;n&#xED; t&#x159;&#xED;dy zm&#x11B;nit, budeme postupovat podobn&#x11B; jako\nu jin&#xFD;ch objekt&#x16F;.\nKdybych cht&#x11B;l cel&#xE9; &#x10D;&#xED;slo, p&#x159;es kter&#xE9; jde iterovat, pod&#x11B;d&#xED;m z <code>int</code>\na p&#x159;edefinuji <code>__iter__</code>.\nPokud chci t&#x159;&#xED;du, p&#x159;es kterou jde iterovat (tedy ne p&#x159;es objekty dan&#xE9;\nt&#x159;&#xED;dy &#x2013; p&#x159;es t&#x159;&#xED;du samotnou!), pod&#x11B;d&#xED;m z <code>type</code> a p&#x159;edefinuji <code>__iter__</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">IterableMeta</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">bases</span><span class=\"p\">,</span> <span class=\"n\">namespace</span><span class=\"p\">):</span>\n        <span class=\"bp\">cls</span><span class=\"o\">.</span><span class=\"n\">items</span> <span class=\"o\">=</span> <span class=\"nb\">sorted</span><span class=\"p\">(</span><span class=\"n\">n</span> <span class=\"k\">for</span> <span class=\"n\">n</span> <span class=\"ow\">in</span> <span class=\"n\">namespace</span>\n                           <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">n</span><span class=\"o\">.</span><span class=\"n\">startswith</span><span class=\"p\">(</span><span class=\"s1\">&apos;__&apos;</span><span class=\"p\">))</span>\n        <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">bases</span><span class=\"p\">,</span> <span class=\"n\">namespace</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__iter__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"nb\">iter</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"o\">.</span><span class=\"n\">items</span><span class=\"p\">)</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">SimpleEnum</span><span class=\"p\">(</span><span class=\"n\">metaclass</span><span class=\"o\">=</span><span class=\"n\">IterableMeta</span><span class=\"p\">):</span>\n    <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"mi\">1</span>\n    <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"mi\">2</span>\n    <span class=\"n\">c</span> <span class=\"o\">=</span> <span class=\"mi\">3</span>\n    <span class=\"n\">d</span> <span class=\"o\">=</span> <span class=\"mi\">4</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">SimpleEnum</span><span class=\"o\">.</span><span class=\"n\">a</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">list</span><span class=\"p\">(</span><span class=\"n\">SimpleEnum</span><span class=\"p\">))</span>\n</pre></div><p>(V metat&#x159;&#xED;d&#x11B; se v&#x11B;t&#x161;inou pou&#x17E;&#xED;v&#xE1; <code>cls</code> m&#xED;sto <code>self</code>, aby bylo jasn&#xE9;, &#x17E;e\ninstance, se kterou pracujeme, je t&#x159;&#xED;da &#x2013; ale to je jen konvence.)</p>\n<p>Metat&#x159;&#xED;dy se d&#x11B;d&#xED;.\nPokud v p&#x159;&#xED;kazu <code>class</code> nezad&#xE1;m explicitn&#x11B; <code>metaclass</code>, pou&#x17E;ije\nse metat&#x159;&#xED;da nadt&#x159;&#xED;dy:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">AnotherEnum</span><span class=\"p\">(</span><span class=\"n\">SimpleEnum</span><span class=\"p\">):</span>\n    <span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n    <span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"mi\">20</span>\n    <span class=\"n\">z</span> <span class=\"o\">=</span> <span class=\"mi\">30</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">AnotherEnum</span><span class=\"o\">.</span><span class=\"n\">a</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">list</span><span class=\"p\">(</span><span class=\"n\">AnotherEnum</span><span class=\"p\">))</span>\n</pre></div><p>T&#xED;mto zp&#x16F;sobem lze vnuknout t&#x159;&#xED;d&#xE1;m magick&#xE9; schopnosti bez toho, aby\nu&#x17E;ivatel na&#x161;&#xED; knihovny musel pou&#x17E;&#xED;t <code>metaclass</code> &#x2013; sta&#x10D;&#xED; mu pod&#x11B;dit z n&#xE1;mi\np&#x159;ipraven&#xE9; t&#x159;&#xED;dy.</p>\n<p>Dal&#x161;&#xED; v&#x11B;c, kterou metat&#x159;&#xED;dy um&#xED;, je p&#x159;ipravit po&#x10D;&#xE1;te&#x10D;n&#xED; jmenn&#xFD; prostor.\nMetoda <code>__init__</code> (nebo <code>__new__</code>) v metat&#x159;&#xED;d&#x11B; norm&#xE1;ln&#x11B; dostane slovn&#xED;k,\nco&#x17E; nemus&#xED; b&#xFD;t v&#x17E;dy to, co pot&#x159;ebuji.\nM&#x16F;&#x17E;u si cht&#xED;t t&#x159;eba &#x201E;zapamatovat&#x201D; po&#x159;ad&#xED;, v jak&#xE9;m byly jednotliv&#xE9; atributy\nvytvo&#x159;eny &#x2013; a slovn&#xED;k toto po&#x159;ad&#xED; neuchov&#xE1;v&#xE1;.</p>\n<p>Na to existuje speci&#xE1;ln&#xED; metoda <code>__prepare__</code>, kter&#xE1; se, kdy&#x17E; na metat&#x159;&#xED;d&#x11B;\nexistuje, zavol&#xE1; pro vytvo&#x159;en&#xED; jmenn&#xE9;ho prostoru:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">collections</span> <span class=\"kn\">import</span> <span class=\"n\">OrderedDict</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">OrderRememberingMeta</span><span class=\"p\">(</span><span class=\"nb\">type</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"nf\">__prepare__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"n\">OrderedDict</span><span class=\"p\">()</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">bases</span><span class=\"p\">,</span> <span class=\"n\">namespace</span><span class=\"p\">):</span>\n        <span class=\"bp\">cls</span><span class=\"o\">.</span><span class=\"n\">items</span> <span class=\"o\">=</span> <span class=\"nb\">list</span><span class=\"p\">(</span><span class=\"n\">namespace</span><span class=\"p\">)</span>\n        <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">bases</span><span class=\"p\">,</span> <span class=\"n\">namespace</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__iter__</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"nb\">iter</span><span class=\"p\">(</span><span class=\"bp\">cls</span><span class=\"o\">.</span><span class=\"n\">items</span><span class=\"p\">)</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">OrderedEnum</span><span class=\"p\">(</span><span class=\"n\">metaclass</span><span class=\"o\">=</span><span class=\"n\">OrderRememberingMeta</span><span class=\"p\">):</span>\n    <span class=\"n\">first</span> <span class=\"o\">=</span> <span class=\"mi\">1</span>\n    <span class=\"n\">second</span> <span class=\"o\">=</span> <span class=\"mi\">2</span>\n    <span class=\"n\">third</span> <span class=\"o\">=</span> <span class=\"mi\">3</span>\n    <span class=\"n\">fourth</span> <span class=\"o\">=</span> <span class=\"mi\">4</span>\n    <span class=\"n\">fifth</span> <span class=\"o\">=</span> <span class=\"mi\">5</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"nb\">list</span><span class=\"p\">(</span><span class=\"n\">OrderedEnum</span><span class=\"p\">))</span>\n</pre></div><p>Toho se d&#xE1; vyu&#x17E;&#xED;t t&#x159;eba v mapov&#xE1;n&#xED; objekt&#x16F; na datab&#xE1;zi (nap&#x159;. v Django Models\nnebo SQLAlchemy), kdy chceme, aby po&#x159;ad&#xED; sloupc&#x16F; tabulky odpov&#xED;dalo\ntomu, jak jsou sloupce/atributy nadefinov&#xE1;ny ve t&#x159;&#xED;d&#x11B;.</p>\n<h2>A dal&#x161;&#xED;</h2>\n<p>Dal&#x161;&#xED; (bohu&#x17E;el?) obl&#xED;ben&#xFD; trik je vnuknut&#xED; magick&#xFD;ch schopnost&#xED; modulu.</p>\n<p>Naimportovan&#xE9; moduly Python ukl&#xE1;d&#xE1; do slovn&#xED;ku <code>sys.modules</code>, aby p&#x159;i dal&#x161;&#xED;m\nimportu nemusel na&#x10D;&#xED;tat znovu &#x2013; <code>sys.modules</code> tedy slou&#x17E;&#xED; jako cache.\nA tuto cache m&#x16F;&#x17E;eme zm&#x11B;nit (tzv. <em>cache poisoning</em>) &#x2013; p&#x159;idat si do n&#xED;\nvlastn&#xED; &#x201E;modul&#x201C;, kter&#xFD; ov&#x161;em v&#x16F;bec nemus&#xED; b&#xFD;t modul, a tud&#xED;&#x17E; m&#x16F;&#x17E;e um&#x11B;t v&#x11B;ci,\nkter&#xE9; moduly norm&#xE1;ln&#x11B; neum&#xED;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">sys</span>\n\n<span class=\"n\">sys</span><span class=\"o\">.</span><span class=\"n\">modules</span><span class=\"p\">[</span><span class=\"s1\">&apos;fake&apos;</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"s1\">&apos;a string&apos;</span>\n\n<span class=\"o\">...</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">fake</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">fake</span><span class=\"p\">[</span><span class=\"mi\">2</span><span class=\"p\">])</span>\n</pre></div><p>Kdy&#x17E; toto ud&#x11B;l&#xE1;me p&#x159;&#xED;mo z modulu, u&#x17E;ivatel na&#x161;&#xED; knihovny dostane podstr&#x10D;en&#xFD;\nobjekt hned p&#x159;i prvn&#xED;m importu.\nK tomu se hod&#xED; prom&#x11B;nn&#xE1; <code>__name__</code>, jm&#xE9;no aktu&#xE1;ln&#xED;ho modulu:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">sys</span><span class=\"o\">.</span><span class=\"n\">modules</span><span class=\"p\">[</span><span class=\"vm\">__name__</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">ReplacementModule</span><span class=\"p\">()</span>\n</pre></div><p>Jin&#xFD; trik je registrace &#x201E;built-in&#x201C; (&#x201E;superglob&#xE1;ln&#xED;&#x201D;) prom&#x11B;nn&#xE9;:</p>\n<div class=\"highlight\"><pre><code>import builtins\nbuiltins.ANSWER = 42\n\n...\n\n# T&#x159;eba v jin&#xE9;m modulu\nprint(ANSWER)</code></pre></div><p>T&#xED;mto zp&#x16F;sobem se daj&#xED; i p&#x159;edefinovat vestav&#x11B;n&#xE9; funkce, co&#x17E; m&#x16F;&#x17E;e b&#xFD;t n&#x11B;kdy\nu&#x17E;ite&#x10D;n&#xE9; pro lad&#x11B;n&#xED;. V produk&#x10D;n&#xED;m k&#xF3;du to ale, pros&#xED;m, ned&#x11B;lejte.</p>\n<h2>&#xDA;kol</h2>\n<p>&#xDA;kol nen&#xED;!</p>\n<p>Budete-li cht&#xED;t n&#x11B;kter&#xE9; techniky z t&#xE9;to lekce ve sv&#xE9;m k&#xF3;du (v&#x10D;etn&#x11B; semestr&#xE1;lky) pou&#x17E;&#xED;t, zamyslete se, jestli se probl&#xE9;m ned&#xE1; vy&#x159;e&#x161;it jednodu&#x161;eji, &#x10D;iteln&#x11B;ji, p&#x159;ehledn&#x11B;ji, udr&#x17E;ovateln&#x11B;ji.\nDobr&#xFD; m&#xE1;g v&#xED;, kdy magii <em>nepou&#x17E;&#xED;t</em>.</p>\n\n\n        "
    }
  }
}