Začnu jinak, než jak jsem minule zamýšlel. Zkusím se rovnou zaměřit na skoro skutečnou situaci. No, dobře, ne úplně skutečnou, ale podobnou tomu, co občas sám dělám. Budu chtít implementovat celý model jako jeden objekt, který mi bude při změně „vystřelovat“ nějaké události. A teď ta skoro skutečná situace: Máme nějakou sadu výrobků. Při přečtení čárového kódu na výrobku chceme zobrazit informace o něm. To znamená postup – seznam operací, kterými se ten výrobek vyrábí, a seznam operací, které na výrobku byly skutečně provedeny. Tak jdeme na to: github/modellerkit, step04
Než vůbec začneme, budeme si muset udělat nový „horký“ objekt – tentokrát opravdu objekt. Budu mu říkat HotObject. Představuju si ho jako objekt, který funguje docela normálně, ale má „horké“ properties. Buď takové, které při změně „vystřelí“ událost, nebo „horké“ kontejnery: HotList, TypedHotList nebo HotObject. Kromě toho bude moct mít i „normální“ properties, které žádné „střílení“ nezpůsobí. Ty „horké“ properties si budeme deklarovat. Je to jako když děláme TypedHotList, ale složitější. Třeba: Můj objekt je HotObject, který má horkou unicode property article, horkou int property sn, TypedHotList process a TypedHotList operations.
V rámci přípravy uděláme objekt Server, který nám bude dávat informace o výrobcích. Hledejte v production.py třídu Server, funkce get_product_ops, která vrátí seznam operací provedených pro výrobek, a get_process, která vrátí postup. S modelem budeme mluvit asi takto: Modele, chci výrobek ABC1234, s/n 653. A teď chci výrobek ABC8562, s/n 21680. Model na základě toho řekne serveru, hele serveře, dej mi data, a nastaví své „horké properties“. To „střílení“ se pak už děje jakoby samo.
Základem HotObject je přiřazování hodnot do „horkých properties“. V principu vypadá asi takto (zkráceno, hotmodel.py: __setattr__):
def __setattr__(self, name, val): if name.startswith("_"): return super(HotObject, self).__setattr__(name, val) if name in self._hot_properties: (type_info, allow_none) = self._hot_properties[name] if issubclass(type_info, HotObject): raise AttributeError("Cannot assign to %s" % name) if getattr(self, name) == val: return if not val is None and not isinstance(val, type_info): raise TypeError( "Only %s allowed for %s" % (type_info.__name__, name), ) ret = super(HotObject, self).__setattr__(name, val) self._fire("update", name) return ret # normal (not hot) properties return super(HotObject, self).__setattr__(name, val)Zaprvé, nevšímáme si ničeho, co začíná _. Pak, „horké brambory“ jsou v self._hot_properties. Ostatní zase zpracováváme normálně. Jestli to tedy je „horká“ property, tak zjistíme, jestli je to HotObject a v tom případě nemůžeme přiřadit přímo do něj. Jinak zkontrolujeme typ, přiřadíme, a vystřelíme.
Teď ještě ta deklarace. Funkcí make_hot_property nastavíme, že žvýkačka je „horká“ (zkráceno).
def make_hot_property(self, name, type_info, allow_none, initial_value): if type_info in IMMUTABLE_TYPES or issubclass(type_info, HotBase): pass else: raise TypeError( "Type not allowed as a hot property: %s" % type_info, ) self._hot_properties[name] = (type_info, allow_none, ) if not initial_value is None and not isinstance( initial_value, type_info, ): raise TypeError( "Only %s allowed for %s" % (type_info.__name__, name), ) super(HotObject, self).__setattr__(name, initial_value) return initial_valueZapíšeme si to do self._hot_properties, nastavíme počáteční hodnotu, a je hotovo. Vynechal jsem nějaké kontroly typů a hodnot.
Věc se pak používá takto:
class ProductModel(hotmodel.HotObject): def __init__(self, server): super(ProductModel, self).__init__() self.server = server self.make_hot_property("article", str, True, None) self.make_hot_property("sn", int ,True, None) self.make_hot_property( "process", hotmodel.TypedHotList, False, hotmodel.TypedHotList(ProcessOperation), ) self.make_hot_property( "operations", hotmodel.TypedHotList, False, hotmodel.TypedHotList(ProductOperation), ) self.make_hot_property("process_selection", int ,True, None) self.make_hot_property("operation_selection", int ,True, None)
Pro zajímavost se podívejte ještě na ProductModel.set_product.
def set_product(self, article, sn): self.article = article self.sn = sn self.process[:] = self.server.get_process(article, sn) self.operations[:] = self.server.get_product_ops(article, sn)Takhle se do modelu „nahraje“ nový výrobek, a model sám „vystřelí“, co se změnilo.
Tím pro dnešek skončíme. Kdo dává pozor, vidí, že je tam něco špatně. Když si ukázku pustíte, uvidíte, že to informace o změnách vypisuje, ale zkuste si na to navěsit nějaké ty „listenery“. Nepůjde to. A na to se podíváme příště.
Ale, to já se zase nezlobím. Ono je to vždycky něco za něco. Když si porovnám způsoby, jakými jsem to dělal (ať už model nehází eventy a view se ho dotazuje, nebo přístup k modelu přes funkce, nebo skoro způsob, který popisuju v článku až na to že se věří že tam nikdo nenarve/nezmění co nemá, nebo v článku popisovaný způsob), tak jsem si vybral tenhle "nádor" :-/
Já ještě nejsem na konci, ale když přežiju to nepythonovskou deklaraci properties, mi to zatím vychází na samá pozitiva a sociální jistoty.
Jmenuju se Petr Blahoš. Programuju něco přes 20 let. Tady se snažím psát hlavně o Pythonu, webovém frameworku Pyramid, a občas i o něčem úplně jiném.
Přečteno 19 224×
Přečteno 11 850×
Přečteno 9 340×
Přečteno 8 799×
Přečteno 8 592×