[python] Statické metody v Pythonu

Petr Prikryl PrikrylP na skil.cz
Středa Listopad 8 15:28:49 CET 2006


Díval jsem se na to znovu a beru zpět to, 
že si myslím, že tohle využití vypadá zajímavě.
 

superman
> [...]Protože v předkovi uhel se počítá s tím, že v metodě 
> __add__ se přes self předává jen jeden argument, najednou 
> si v potomku začne stěžovat na dva. Python má v tomhle 
> asi nepořádek a asi nesnáší takové šťoury jako jsem já. 
> Takže závěr nikdy nezkoušejte předefinovat statickou 
> metodu nestatickou, protože Python si s tím neví 
> rady a začne hlásit chyby i tam, kde by to v jiných 
> jazycích bylo v pořádku. Never is perfect :-)))
> [myšleno asi Nobody is perfect -- Někdo to rád horké]
 
> class uhel:
> 
>      @staticmethod
>      def static(a):
>          print "trida1.static(): ", a
> 
>      def method(self):
>          self.static(1)
> 
> class zemepisny_uhel(uhel):
> 
>      #@staticmethod
>      def static(a):
>          print "trida2.static(): ", a
> 
> a = uhel()
> a.method()
> 
> b = zemepisny_uhel()
> b.method()
> 
> trida1.static():  1
> Traceback (most recent call last):
>    File "C:\home\astrol\test.py", line 25, in ?
>      b.method()
>    File "C:\home\astrol\test.py", line 10, in method
>      self.static(1)
> TypeError: static() takes exactly 1 argument (2 given)
> 
> Závěr: Python se chová tak, jako kdyby definici třídy znovu 
> překládal do definice potomka. Zápis self.static přeloží 
> v uhel.method jako volání statické metody s jedním parametrem, 
> zatímco v zemepisny_uhel jako volání normální metody se dvěma 
> parametry. Řekl bych, že tato feature je dost error prone, 
> a že vlastnost "zapouzdření" dostává pěkně na zadek.

Já na tom nevidím nic divného. V části 

> class zemepisny_uhel(uhel):
>      #@staticmethod
>      def static(a):
>          print "trida2.static(): ", a

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)

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")))

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

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. 

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

pepr


Další informace o konferenci Python