POO Herència
Herència és el mecanisme pel qual podem crear objectes (fills) que adopten les característiques d'un altre (pare) i que les extenen o sobreescriuen amb nous mètodes o atributs.
Contingut
Herència bàsica
En Python la herència s'introdueix entre parèntesi. En aquest exemple Pilota() no hereda de ningú i PilotaDeTennis hereda les propietats (atributs i mètodes) de Pilota.
class Pilota():
# atributs
velocitat = 10
posx = 10
posy = 12
# mètodes
def accelera(self):
self.velocitat = self.velocitat + 5class Pilota():
class PilotaDeTennis(Pilota):
color = "verd"
def accelera(self):
self.velocitat = self.velocitat + 12
Tinguem en compte que:
- PilotaDeTenis hereda els atributs posx, posy i velocitat de Pilota.
- Sobreescrivim el mètode accelera(), i enlloc d'accelerar 5 unitats (el mètode definit en Pilota), la PilotaDeTennis accelerarà 20 unitats.
>>> pelo = Pilota() >>> ptennis = PilotaDeTennis() >>> pelo.velocitat 10 >>> pelo.accelera() >>> pelo.velocitat 15 # Pilota ha accelerat 5 punts >>> ptennis.velocitat 10 >>> ptennis.accelera() >>> ptennis.velocitat 22 # PilotaDeTennis ha accelerat 12 punts
"Noves classes" de Python
Llegir: http://docs.python.org/reference/datamodel.html#new-style-and-classic-classes
A partir de la versió 2.1 tots els objectes els heredem de "object". Es solventen alguns problemes antics. Es podria haver posat per defecte, però s'ha mantingut per la compatibilitat amb les aplicacions anteriors.
L' "estil antic" de classes Python (prèvies a 2.1) es defineixen:
class Pilota():
...
Ara, per definir un objecte "a la manera nova": OJU! sempre ho farem així en principi
class Pilota(object):
...
La diferència apreciable més important son els mètodes que heredem de "object":
>>> dir(Pilota) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'accelera', 'posicio', 'posx', 'posy', 'velocitat']
Constructors i herència: instrucció super()
Quan creem una classe derivada i sobreescrivim el constructor (mètode __init__ en Python), el constructor del pare no es crida. Per exemple:
class A(object):
def __init__(self):
print "construint l'objecte..."
class B(A):
def __init__(self):
print "hola què tal?"
Ara comprovem què passa:
>>> a = A() construint l'objecte... >>> >>> b = B() hola què tal?
Com podem veure, el constructor de A() no es crida al crear un objecte B(). Com que molt sovint es necessita cridar-lo, es pot fer amb la instrucció super(). Oju que aquest exemple és per Python 2.x. En Python 3.0 s'ha simplificat a super(), sense arguments, molt més elegant:
# exemple en Python 2.x
class A(object):
def __init__(self):
print "construint l'objecte..."
class B(A):
def __init__(self):
super(B,self).__init__()
print "hola què tal?"
ULL!: si no creem l'objecte base A(object) heredat de "object", l'exemple no funcionarà.
Comprovem-ho:
>>> a = A() construint l'objecte... >>> b = B() construint l'objecte... hola què tal?
Herència múltiple
...
Polimorfisme
El polimorfisme és una important propietat de la POO, molt rellevant sobretot en llenguatges on no hi ha tipus dinàmics com el C++ o el Java. El què significa és que una referència (punter) a un objecte genèric (pare), al cridar a una funció que està sobrescrita per un objecte derivat, executarà la funció més "nova", és a dir, la de l'objecte instanciat realment.
El cas típic és tenir un array de punters a objectes pare que no sabem de quin tipus derivat exactament son, per exemple, un array de Pilotes. Segons com l'haguem creat, el punters apuntaran a objectes dels tiups PilotaDeTennis, PilotaDeFutbol i PilotaDeBasket, però no tenim perquè saber-ho, tot i que sabem segur que tots hereden de Pilota. Quan cridem al mètode accelera() s'executarà el de l'objecte derivat, no el del pare genèric, encara que el punter apunti a Pilota.
En el cas del Python no ens resulta molt rellevant ja que tenim tipus dinàmics amb total introspecció. Això vol dir que en tot moment sabem de quin tipus és cada objecte. A sobre, en Python podem tenir arrays d'objectes heterogenis, cosa que no es pot fer amb Java o C++. Així, el polimorfisme no en aquests llenguates és molt important, però no en Python.
Per saber més podeu llegir:
- l'article sobre polimorfisme a la Wikipèdia.
- POO: Polimorfisme a cacauet.
Mètodes i classes virtuals
En Python tampoc és molt rellevant aquest aparat ja que està relacionat amb el Polimorfisme.
Un mètode virtual és aquell que, en ser cridat, intentarà cridar al de la classe derivada en cas que existeixi. Si no definim com a virtual un mètode es cridarà al de la classe del punter referenciat. Podeu veure l'exemple en C++ a POO: Polimorfisme.
Un mètode virtual pur és aquell mètode virtual que no s'ha definit. En C++:
class Pilota {
...
virtual int accelera()=0;
...
}
Una classe virtual és aquella que al menys té un mètode virtual pur. Una classe virtual no es pot instanciar. Cal crear una classe derivada que implementi el/s mètode/s virtual/s per poder ser instanciada.