[python] Statické metody v Pythonu

superman feed na centrum.cz
Středa Listopad 8 15:56:28 CET 2006


>>jazycích bylo v pořádku. Never is perfect :-)))
>>[myšleno asi Nobody is perfect -- Někdo to rád horké]

Přesně tak :-) Díky za opravu, příště si dám pozor na cizí termity :-)

> je dekorátor zapoznámkovaný, takže je to stejné, jako kdybych
> napsal 
> 
>  class zemepisny_uhel(uhel):
>       def static(a):
>           print "trida2.static(): ", a
> 
> Což je stejné jako
> 
>  class zemepisny_uhel(uhel):
>       def static(self):
>           print "trida2.static(): ", self
> 
> Jenom jsi porušil konvenci, že první argument metody se má
> pojmenovat self. No a druhý argument tam není. Myslím,
> že interpret Pythonu v tom docela jasno MÁ ;o)

Má, tohle je opravdu moje chyba. Už mě na to upozornil člověk z 
předchozího mailu.

Jen prostě mě nenadchlo, že v předchozí třídě to byla statická metoda, 
počítalo se s ní jako se statickou metodou a pokud to v potomkovi změním 
na dynamickou, že to má zpětné důsledky i do předka nejen ohledně 
chování, ale vlastně i z hlediska interface. Ale to je otázkou toho, že 
jsme v dynamickém jazyce. Takže je to asi v pořádku.

> A ještě k tomu použití statické metody. Možná máš znalosti
> C++, kde se zápis typu 
> 
> zem_uhel + "30N54" 
> 
> typicky dosahuje přetížením operátoru +  a typicky se
> implementuje tak, že se na místě druhého argumentu očekává
> konstantní reference a třídu zemepisny_uhel. Jenže 
> u třídy zemepisny_uhel by se v takovém případě typicky 
> definoval konstruktor, který bere odkaz na konstantní string
> a může se tam tedy dosadit i literál. Překladač pak automaticky
> udělá tohle:
> 
> zem_uhel + zemepisny_uhel("30N54")
> 
> tj. nejdříve zkonstruuje pomocný objekt pro druhý argument.
> Dále se to dá rozepsat jako
> 
> zem_uhel.operator+(zemepisny_uhel(("30N54")))

No, C++ je velmi flexibilní jazyk, takže buď C++ zavolá konstruktor, a 
nebo operátor +, kde druhý parametr je string, pokud takový existuje. Ta 
první varianta s voláním konstruktoru je "drahá", tedy z nouze ctnost, 
protože se zbytečně vytváří dočasná instance, a pak zase ruší. Operátor 
+ to většinou dokáže efektivněji s mnohem menší režií.

> Python ale implicitně podobný konstruktor nezavolá. Podle 
> mého by tedy měla být třída uhel vytvořena tak, aby její
> složka __radians jednoduše obsahovala platnou hodnotu, nebo
> by se měla definovat metoda, která hodnotu v radiánech
> vrací. Metoda __add__ by jako argument měla brát instanci
> třídy uhel a udělat jednoduše
> 
> class uhel:
>     def __init__(self, radian):
>         self.__rad = radian
> 
>     def __add__(self, u):
>         return self.__rad + u.__rad
> 
>     def __str__(self):
>         return str(self.__rad)
> 
> 
> u1 = uhel(1.0)
> u2 = uhel(2.0)
> 
> print u1
> print u2
> 
> u3 = u1 + u2
> u4 = u1 + uhel(3.0)
> 
> print u3
> print u4

To je přesně stejný případ jako s tím C++. Tohle řešení, které uvádíte 
je naprosto nejelegantnější a nejčistější. Myslím, že nic lepšího asi 
nelze vymyslet, ale vzniká tu instance třídy uhel, která vlastně 
vzniknout nemusí. Řekněme, že je to takový relikt v mém myšlení, který 
mě nutí se občas zabývat efektivitou programu a který mi říká, že 
vytvoření instance třídy uhel není potřeba. Proto jsem zavedl tu 
statickou metodu a upravil jsem __add__ tak, aby přijímal i čísla a stringy.

Tohle spočte součet bez toho, aniž by se vytvřila dočasná instance třídy 
uhel:

u4 = u1 + 3.0

A tohle udělá to samé, ale vytvoří se (a pak posléze zruší) navíc 
instance třidy uhel:

u4 = u1 + uhel(3.0)

Prostě nevím a netuším, jestli tuhle režii vytvoření instance navíc mám 
vzít v úvahu. Pokud jí připustím, mám asi čistější kód, pokud zase 
pomůžu metodě __add__, aby uměla sečíst číslo přímo, mám efektivnější 
kód. Naproti tomu zase hrozí, že sečtu něco, co se jako číslo bude 
tvářit a významově to nemá s úhlem nic společného a můžu hledat chyby. 
Nevím, který postup je správný, asi každý má něco.

> Třída zemepisny_uhel by byla odvozená v tom smyslu, 
> že by měla jinou implementaci __init__(), ve které
> by se mohly rozpoznávat různé formy předaného argumentu.
> Pak by to mohlo vypadat takhle:
> 
> class uhel:
>     def __init__(self, radian):
>         self.__rad = radian
> 
>     def __add__(self, u):
>         return self.__rad + u.__rad
> 
>     def __str__(self):
>         return str(self.__rad)
> 
> 
> class zemepisny_uhel(uhel):
>     def __init__(self, s):
>         uhel.__init__(self, float(s))
> 
> u1 = uhel(1.0)
> u2 = uhel(2.0)
> 
> print u1
> print u2
> 
> u3 = u1 + u2
> u4 = u1 + uhel(3.0)
> 
> print u3
> print u4
> 
> z = zemepisny_uhel('4.0')
> 
> print z
> print u1 + z
> print z + u2
> 
> Uplatnění pro statické metody jsem v tom příkladu
> zatím nenašel.

Já také ne. Jenže u mě ta statická metoda pro vytvoření úhlu vypadá 
takto a taky sežere až neuvěřitelně divoké definice úhlů od zadání úhlu 
v radiánech až po různé se stupni, minutami, atd.. Skoro jsem na svojí 
metodu hrdý :-))):

_MathAngleRegExpStr = \
     u'(^\\s*' + \
     u'([0-9]*[.]?[0-9]*)\\s*' + \
     u'((?:[Rr](?:[Aa][Dd](?:[Ss]|[Ii][Aa][Nn][Ss])?))\\s*)*' + \
     u'\\s*$)' + \
     u'|' + \
     u'(^\\s*' + \
     u'([0-9]*[.]?[0-9]*)\\s*' + \
 
u'(?:(?:[°]|[Dd](?:[Ee][Gg](?:[Rr]|(?:[Ee][Ee]))?[Ss]?)?)\\s*)*\\s*' + \
     u'([0-9]*[.]?[0-9]*)\\s*' + \
     u'(?:(?:[\']|(?:[Mm](?:[Ii][Nn](?:[Uu][Tt][Ee])?[Ss]?)))\\s*)*\\s*' + \
     u'([0-9]*[.]?[0-9]*)\\s*' + \
 
u'(?:(?:[\"]|(?:[Ss](?:[Ee][Cc](?:[Oo][Nn][Dd])?[Ss]?)?))\\s*)*\\s*' + \
     u'[.]?([0-9]*)' + \
     u'\\s*$)'


_MathAngleRegExp = re.compile(_MathAngleRegExpStr, re.UNICODE)


class Angle(object):

   @staticmethod
   def _s_ConvertStringToRadians(angle_string):
     angle_string = unicode(angle_string)
     reg_exp_result = _MathAngleRegExp.findall(angle_string)
     if (not isinstance(reg_exp_result, list)) \
       or (len(reg_exp_result) <= 0):
       raise ValueError
     reg_exp_result = reg_exp_result[0]
     if not isinstance(reg_exp_result, tuple):
       raise ValueError
      # atd.

A pak máte potomky, které jsou schopny sežrat ještě další konkrétní 
formáty stringy odle určení.

> Poznámka: kromě staticmethod() a příslušného
> dekorátoru existuje i classmethod(), ke které 
> jsme se zatím nedostali.

Uf, dekorátory mě zajímají. V manuálu jsem našel tak akorát velké nic, 
takže informace na toto téma vítám. Co dělá classmethod?

Miloslav Ponkrác





Další informace o konferenci Python