[python] Statické metody v Pythonu

superman feed na centrum.cz
Středa Listopad 8 17:45:55 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