[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