Python és az OOP

Ahogy már írtam, a python egy objektum orientált scriptnyelv, de nem vagyunk rákényszerítve az OOP-re, nyugodtan használhatjuk imperatív/procedurális módon, azaz kb. mintha perl- vagy shellscriptet írnánk. Nem kell main() függvény (mint C-ben), nem kell egy kvázi felesleges osztály egy public static void main(...) metódussal, szépen sorról sorra megcsinálja, amit mondunk neki.

Pár soros scriptecskénél ez hasznos tulajdonság, nagyobb projectekben viszont kerülendő.

Csak címszavakban, mi is az az objektum orientált programozás?

A világunkban lévő dolgokat az OOP-ben objektumok személyesítik meg. A dolgok tulajdonságai lesznek az objektumok attribútumai, a műveletek pedig - amire az adott dolog képes -, a metódusok.

Például vegyük Hápit, a kacsát. Hápi színe barna, kettő lába van (attribútumok), tud úszni és hápogni (metódusok).

Az objektumok típusát osztálynak hívjuk, az objektumot az osztály egy példányának. Az attribútumokat és a metódusokat az osztályoknál definiáljuk, természetesen az osztályból létrehozott példányok rendelkeznek majd ezekből egy-egy sajáttal. Az osztályok hierarchikusan épülhetnek egymásra - pl. a kacsák állatok -, ezt hívjuk öröklődésnek, pythonban egy osztálynak több őse is lehet. A gyermek osztály örökli a szülő tulajdonságait.

Hápi példáját nézzük is meg python nyelven:

class Allat(object):
  """Allat osztaly
  az Allat egy olyan valami, aminek vannak labai, es tud "beszelni"
  """

  lab = None

  # az __init__ nevu metodus hivodik meg az objektumok peldanyositasakor
  # OOP-ben konstruktor a neve, de pythonban ez kicsit arnyaltabb
  # mindenesetre itt vegezzuk a objektum inicializalasat
  # a `self` maga az objektum, a `lab` pedig a labak szama, amit kotelezo megadni
  def __init__(self, lab):
    self.lab = lab

  def beszel(self):
    print "valami hang hallatszik"

class Kacsa(Allat):
  """Kacsa osztaly
  a Kacsa egy ketlabu Allat, ami alapesetben barna, de mas szinu is lehet
  a Kacsak hapogva beszelnek
  """

  szin = None

  # a kacsak szine alapbol barna, ha nem adjuk meg
  def __init__(self, szin='barna'):
    self.szin = szin
    # meghivja az ososztaly (Allat) konstruktorat, a labakat 2-re allitva
    super(Kacsa, self).__init__(2)

  # felulirja az ososztaly `beszel` metodusat, a kacsa hapog
  def beszel(self):
    self.hap()

  # a kacsa tud hapogni
  def hap(self):
    print "Hap, hap!"

  hapi = Kacsa()
  print hapi.szin # 'barna'
  hapi.beszel() # "Hap, hap!"

  hazihapi = Kacsa('feher')
  print hazihapi.szin # 'feher'

Elsőre lehet nem hangzik túl egyszerűen, pedig az :)

Sok más nyelvvel ellentétben a python nem statikusan, hanem dinamikusan, viszont erősen típusos. De ez kb. mindegy is, mert típus ellenőrzés csak akkor van, ha kézzel csinálunk :) Egyesek szerint ez hátrány, mások szerint előny. Lássunk egy példát, ami ezt a tulajdonságot használja ki, és a szakirodalomban "duck typing" néven találkozhatunk vele ("kacsa tipizálás"? inkább nem fordítanám le).

Tegyük fel, hogy van egy függvényünk, ami háromszor egymás után hápogtatja a neki átadott kacsát. Mivel pythonban nem tudjuk megmondani, hogy a bejövő paraméterünk Kacsa osztálybeli legyen, vagy esetleg valósítsa meg a HápogniTud interfészt (jáva módra), de bátrak vagyunk, illetve majd nem hívjuk meg olyannal, ami nem tud hápogni, ezt csináljuk:

def haromhap(kacsa):
  kacsa.hap()
  kacsa.hap()
  kacsa.hap()

Persze ha akarjuk le tudjuk ellenőrizni, hogy amit kaptunk, az tud-e hápogni, ha de így alapból is ha valami olyannal hívjuk, ami nem tud, akkor kapunk egy exceptiont (kivételt...). (A már hozzászólást fogalmazó lelkes jávások vajh mindenhova odaírják-e, hogy if (null == bejovoparameter) ....)

Viszont így a kezünk sincs megkötve, hogy csak Kacsával vagy HápogniTuddal hívhatjuk a függvényt, hanem bármivel, aminek van hap() metódusa. Pl:

>>> def hap():
...   print "fake hap"
...
>>> nemkacsa = Allat(4)
>>> nemkacsa.hap = hap
>>> haromhap(hapi)
Hap, hap!
Hap, hap!
Hap, hap!
>>> haromhap(nemkacsa)
fake hap
fake hap
fake hap

Pedig nemkacsa nem Kacsa :)

Pythonban egyébként minden objektum, a függvények, a modulok, még az osztályok maguk is, és ez néha nagyon hasznos tud lenni.

07aug.

9 Comment for Python és az OOP

  1. Gábor says:

    Mi az az object szó a class Allat(object): sorban? Mi az a None és miért kell a lab = None? Ha az Állat __init__ -nél két paraméter van akkor, hogy hogy elegendő egy Állatot egy argumentummal meghívni?

    • RePa says:
      • a class Allat(object) azt jelenti, hogy az Allat osztaly az object ososztalybol szarmazik. python 3-ban mar ez az alapertelmezett, python 2 meg nem, es a super object eseten mukodik jol

      • a None az kb. a null/nil pythonos megfeleloje, o maga is objektum

      • az osztaly deklaraciojanal azert irtam ki, hogy lab = None, hogy lassuk elore, hogy lesz ilyen attributum, egyebkent nem kotelezo odairni, amikor erteket adunk egy uj attributumnak, akkor az automatikusan letrejon (es azert None, mert meg nem tudjuk abban a pillanatban az erteket)

      • az __init__-nek, mint minden metodusnak (a beszel-nek is) van egy elso, altalaban self-nek hivott parametere, ami maga az objektum, amihez tartozik, ezt hivaskor nem kell kiirni (javaban pl. ez a this, ami valami varazslatos uton belekerul a metodus latoterebe). Ezt a self-et persze hivhatjuk barhogy, a lenyeg, hogy az elso bejovo parameter. Statikus metodusok (@staticmethod dekoratorral) nem kapnak ilyet, osztaly szintu metodusok (@classmethod dekoratorral) pedig az osztalyobjektumot kapjak meg.

      • RePa says:

        kicsit erdekesen formazta a listat :)

      • AntiText says:

        Szia ! És ha már szóltál a dekorátorokról, tényleg kíváncsi lennek, még is mi az a dekorátor és mire használatos mert nem igazán a találtam erről adatot a magyar nyelvű weben.

        • RePa says:

          Ertettem, de akkor errol majd bovebben, nem commentben.

          Egy kis turelmet kerek, mert most indulunk nyaralni, par nap szunet kovetkezik :)

      • Gábor says:

        Nem tudom túltenni magam ezen, hogy ott a self a függvénydeklarációnál, de meghívásnál mégsem kell kiírni. Akkor nem értem mit befolyásol, mi van ha oda mást írsz?

        • RePa says:

          Pythonban egy metodusbol az objektumot, amihez az adott metodus tartozik ezen a self nevu valtozon keresztul tudod elerni. (Igazabol barhogy hivhatod, a lenyeg, hogy explicit modon meg kell adni.) Amikor deklaralod, akkor az a fuggveny deklaracio az osztalyhoz tartozik, amikor viszont az osztalyt peldanyositod, akkor a peldanyhoz tartozo metodus mar nem pont ugyanaz a fuggveny lesz, illetve kb. ugyanaz lesz, csak nem fuggveny, hanem metodus, az adott objektumhoz kotve, es meghivasakor az objektumot kapja automatikusan elso parameterkent.

          Mas nyelvekben ez lehet implicit (pl. this), es a metodusok/attributumok lathatosaga is mas, mint pythonban, azok elore deklaralva vannak, mig pythonban ez nincs igy, es pl. egy metoduson belul uj attributumot adhatsz az objektumhoz, es egyebek.

          Egyebkent ha angolul tudsz, akkor ezeket at lehet futni a temaban: http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#from-function-to-method http://stackoverflow.com/questions/2709821/python-self-explained

  2. slink says:

    Úgy tudom, az osztály deklaráció elején szereplő ` lab = None`-t csak akkor javallott használni, ha azt osztályváltozóként is el akarjuk érni, ellenkező esetben csak a konstruktorban inicializálunk.

    >>> class A(object):
    ...     a = False
    ...     def __init__(self):
    ...             self.a = True
    ...
    >>> A()
    <__main__.A object at 0x10042a210>
    >>> A().a
    True
    >>> A.a
    False
    >>> class B(object):
    ...     def __init__(self):
    ...             self.b = True
    ...
    >>> B().b
    True
    >>> B.b
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: type object 'B' has no attribute 'b'

    Persze ezt __get__-tel is tudod szabályozni.

    • RePa says:
      jogos, csak a mar OOP tapasztalattal rendelkezoknek akartam egy kis segitseget adni. a ``getattr`` es baratai pedig olyan finomsagok, amikkel nem akarom megzavarni a kezdoket.

Szólj hozzá