Komentáře k článku

Python profesionálně: metatřídy

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

Zpět na článek

22 komentářů k článku Python profesionálně: metatřídy:

  1. pavel s.

    autorovi

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

    class Roman(int): pass

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

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

    1. Ivan Nový

      No to je sice nesmysl,

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

        1. Ivan Nový

          Re: No to je sice nesmysl,

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

          1. pavel s.

            Re: No to je sice nesmysl,

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

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

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

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

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

            1. jen tak

              Re: asi tak nejak ?

              class Roman(int):

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

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

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

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

              1. Opravdový odborník :-)

                Třída vs. funkce

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

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

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

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

  2. FiŠ

    konstruktor

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

    1. Michal HořejšekAutor příspěvku

      Re: konstruktor

      http://mail.python.org/pipermail/tutor/2008-April/061426.html

      Use __new__ when you need to control the creation of a new instance.
      Use __init__ when you need to control initialization of a new instance.

      __new__ is the first step of instance creation. It’s called first,
      and is responsible for returning a new instance of your class. In
      contrast, __init__ doesn’t return anything; it’s only responsible for
      initializing the instance after it’s been created.

      In general, you shouldn’t need to override __new__ unless you’re
      subclassing an immutable type like str, int, unicode or tuple.

  3. Honza Kral

    Skutecne neprofesionalni programovani

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

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

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

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

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

    1. Michal HořejšekAutor příspěvku

      Re: Skutecne neprofesionalni programovani

      Prosím, přečti si ještě jednou ten odstavec pod kódem, kde jsem použil hasattr(attribute, '__call__') místo callable(). (V Pythonu 3.0 a 3.1 metoda callable není.)

      CamelCase a C místo cls jsou překlepy, díky za upozornění. CamelCase používám všude a i když jsem ty články procházel několikrát, stejně mi tam pokaždé něco uteklo.

      Když už píšeš, že je zcela zbytečné generovat ty metody dynamicky, možná by jsi mohl poučit a napsat, jak to udělat bez toho.

      Vím, že když je definovaná, že se přepíše. Zatím mě to netrápilo, takže jsem se nedokopal to upravit (je to ale v plánu). A řešit takovéhle detaily v ukázce mi přijde velmi zbytečné. Stačí se podívat, že tam neřeším ani jiné detaily.

      V mém případě jsem opravdu nepotřeboval tracer, ani profilter, ani debugger, ani coverage.py. Resp. tracer nebo nějaký debugger by se mi hodil, ale ani jedno nešlo použít. Takže jsem si napsal pár těhle speciálních metod, který mi s hledáním problému pomohly. Když už jsem to mě napsané, napsal jsem si to do metatřídy, abych to případně příště už nemusel psát znovu. Mimochodem mluvit zde o testovatelnosti nebo vykonnosti je hloupost – kdo by to použil pro něco jiného, než debugování?

      1. yad

        Re: Skutecne neprofesionalni programovani

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

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

      2. Honza Kral

        Re: Skutecne neprofesionalni programovani

        Za callable se omlouvam, prehledl jsem tu zminku.

        Bez dynamickeho generovani:

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

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

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

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

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

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

        1. Honza Kral

          Re: Skutecne neprofesionalni programovani

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

        2. jen tak

          Re: Skutecne neprofesionalni programovani

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

          1. Martin Hassman

            Re: Skutecne neprofesionalni programovani

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

          2. Honza Kral

            Re: Skutecne neprofesionalni programovani

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

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

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

        3. Michal HořejšekAutor příspěvku

          Re: Skutecne neprofesionalni programovani

          V pohodě, vyznám se v tom. :)

          Musím ty metody generovat, protože pokaždé mám jinou třídu. Mohu sice použít pro zavolání přímo object místo super(cls, self), jak píšeš, jenže když to bude poděděná třída ze třídy, která má nějakou takovou metodu, nikdy se nezavolá. To je to samé, jako že momentálně přepisuju takovou metodu ve tříde, kde metatřídu použiju (což mám už dlouho v plánu tento nedostatek odstranit).

          Když debuguju, rozbíjím kód. Naschvál. Většinou se mi stačí dívat do logu, do kódu, nebo si přidat nějaký výpis někam. Občas to chce ale něco víc a to už rozbíjím kód. Vyřazuju různé části a hledám, co je asi příčinou. Při debugování klidně rozbiju několik metod, abych se dopátral k příčině problému. (Aby bylo jasno – mluvím o hledání nějaké pekelnosti. Kde mám dobré pokrytí testů a nehledám záludnosti, stačí mi testy.)

          __setattr__/ __ge­tattr__ už bylo řečeno.

          Třída má svou metatřídu. To už je horší. Na druhou stranu kolik tříd to má? Těch je tak málo, že se s tím normální vývojář nesetká a vývojář Djanga si umí poradit. Navíc u vývoje Djanga něco takového (podle mne) není potřeba.

          uncidoe. Hm, co s ním? Že se nezobrazí zrovna čitelně?

          stdout. Proto lze logovací metodu vyměnit (já například využil něco jiného; s printem bych nepochodil). Dal jsem tam default print, ale máš pravdu, že logging by byla lepší volba.

          Proč nešlo použít tracer či debuger? Protože šlo o něco jako mod_python. O webovku, kde se při určitém hmatu něco v nějakém stavu stalo a nebylo jasné kde a co. Můj debugger má výhodu v tom, že si ho můžu přidat jen na třídy, kde mám podezření, a ještě filtrovat dle regulerního výrazu. Tedy mohu sledovat změnu stavu jen u toho, co mě zajímá.

          Vím, že ten debugger není dokonalý a ještě potřebuje poladit. Ani nebude dokonalý. Je to jen další pomocník, který jde na to trochu jinak s řešením jiného problému. A jako ukázka, kde je například vhodné použít metatřídu, je více než dobré. Takže ne, nestydím se za to.

      3. Ales Zoulek

        Re: Skutecne neprofesionalni programovani

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

        To nemyslis uplne vazne, ze ne?

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

Napsat komentář

Tato diskuse je již příliš stará, pravděpodobně již vám nikdo neodpoví. Pokud se chcete na něco zeptat, použijte diskusní server Devel.cz

Zdroj: https://www.zdrojak.cz/?p=3657