Nasleđivanje

Klase ne bi mogle da imaju pravu vrednost ako ne bi podržavale nasleđivanje. Sintaksa za definisanje izvedene klase izgleda ovako:

  class DerivedClassName(BaseClassName):
      <statement-1>
.
      .
      .
      <statement-N>

Klasa BaseClassName mora biti definisana unutar scope-a izvedene klase. Umesto imana bazične klase, moguči su i drugi izrazi. To može biti pogodno, na primer, kada je bazična klasa u drugom modulu:

class DerivedClassName(modname.BaseClassName):

Izvršavanje definicije za izvedenu klasu odvija se na isti način kao i za bazične klase, Kada se kreira “klasa” objekat pamti se i bazična klasa.

To se koristi da se razreši pitanje referenci na atribute: ako zahtevani atribut nije u klasi koja se definiše, pretraga se nastavlja u bazičnoj klasi. Ovo pravilo se primenjuje rekurzivno ako je i bazična klasa izvedena iz neke druge klase.

Nema ničeg posebnog u kreiranju instanci izvedene klase: DerivedClassName() kreira novu instancu klase. Referenca na metod rešava se na sledeći način: pretražuju se odgovarajći atributi, a ako treba i spuštanjem naniže u bazičnu klasu, a referenca na metod je validna ako rezultira objektom tipa funkcija.

Izvedena klasa može da “prekrije” metode bazične klase. Pošto metode nemaju specijalne privilegije kada pozivaju druge metode iste klase, može se desiti da metod bazične klase koji poziva drugi metod definisan u istoja bazičnoj klasi završi pozivanjem metoda iz izvedene klase koji je prekrio ciljni metod.

Prekrivajuči metod u izvedenoj klasi može da ima za cilj proširenje, a ne samo zamenu, metoda iz bazične klase. Postoji jedan prost način da se pozove bazični metod direktno: samo treba pozvati BaseClassName.methodname(self, arguments).

Python ima dve ugrađene (built-in) funkcije koje rade sa nasleđivanjem:

  • Funkcija instance za proveru tipa neke instance: instance(obj, int) će biti True samo ako je obj.__class__ jednako int ili neka klasa izvedena iz klase int.
  • Funkcija issubclass za proveru nasleđivanja: issubclass(bool, int) je True pošto je klasa bool subklasa klase int. Međutim, issubclass(float, int) je False pošto float nije subklasa klase int.

Višestruko nasleđivanje

Python podržava jednu formu višestrukog nasleđivanja. Definicija klase sa nasleđivanjem od više bazičnih klasa izgleda ovako:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

Pojednostavljeno, možemo smatrati da se pretraživanje atributa nasleđenih od “parent” klasa odvija po principu “depth-first”, “left-to right”.

Tako, ako atribut nije nađen u klasi DerivedClassName prvo se traži u klasi Base1, a onda rekurzivno u baznoj klasi klase Baze1, pa ako nije pronađen pretraga se nastavlja na sličan način u klasi Base2 itd.

U stvari, postupak je malo komplikovaniji nego što je opisano. Za detalniji opis pogledajte u https://www.python.org/download/releases/2.3/mro/.

Primeri sa nasleđivanjem

Kreiranje subklase

Sintaksa:

class SubClass(SuperClass):
  # data fields
  # instance methods

Primer:

class Vehicle:

    def __init__(self, name, color):
        self.__name = name      # __name is private to Vehicle class
        self.__color = color

    def getColor(self):         # getColor() function is accessible to class Car
        return self.__color

    def setColor(self, color):  # setColor is accessible outside the class
        self.__color = color

    def getName(self):          # getName() is accessible outside the class
        return self.__name

class Car(Vehicle):

    def __init__(self, name, color, model):
        # call parent constructor to set name and color
        super().__init__(name, color)
        self.__model = model

    def getDescription(self):
        return self.getName() + self.__model + " in " + self.getColor() + " color"

# in method getDescrition we are able to call getName(), getColor() because they are
# accessible to child class through inheritance

c = Car("Ford Mustang", "red", "GT350")
print(c.getDescription())
print(c.getName()) # car has no method getName() but it is accessible through class Vehicle

Višestruko nasleđivanje

Sintaksa:

class Subclass(SuperClass1, SuperClass2, ...):
   # initializer
   # methods

Primer:

class MySuperClass1():

    def method_super1(self):
        print("method_super1 method called")

class MySuperClass2():

    def method_super2(self):
        print("method_super2 method called")

class ChildClass(MySuperClass1, MySuperClass2):

    def child_method(self):
        print("child method")

c = ChildClass()
c.method_super1()
c.method_super2()

Prekrivanje (override) metoda

Primer:

class A():

    def __init__(self):
        self.__x = 1

    def m1(self):
        print("m1 from A")

class B(A):

    def __init__(self):
        self.__y = 1

    def m1(self):
        print("m1 from B")

c = B()
c.m1()

Zadaci

1. For this exercise, we want you to describe a generic superclass and at least three subclasses of that superclass, listing at least two attributes that each class would have. It’s easiest to simply describe a real-world object in this manner. An example of what we’re looking for would be to describe a generic Shoe class and some specific subclasses with attributes that they might have, as shown here:

class Shoe:
    #Attributes: self.color, self.brand

class Converse(Shoe): # Inherits from Shoe
    #Attributes: self.lowOrHighTop, self.tongueColor, self.brand = "Converse"

class CombatBoot(Shoe): # Inherits from Shoe
    #Attributes: self.militaryBranch, self.DesertOrJungle

class Sandal(Shoe): # Inherits from Shoe
    #Attributes: self.openOrClosedToe, self.waterproof

You can use any real-world object except a shoe for this problem :)

  1. Consider the following code:

    class Spell:
        def __init__(self, incantation, name):
            self.name = name
            self.incantation = incantation
        def __str__(self):
            return self.name + ’ ’ + self.incantation + ’\n’ + self.get_description()
        def get_description(self):
            return ’No description’
        def execute(self):
            print self.incantation
    
    class Accio(Spell):
        def __init__(self):
            Spell.__init__(self, ’Accio’, ’Summoning Charm’)
    
    class Confundo(Spell):
        def __init__(self):
            Spell.__init__(self, ’Confundo’, ’Confundus Charm’)
        def get_description(self):
            return ’Causes the victim to become confused and befuddled.’
        def study_spell(spell):
            print spell
    
    spell = Accio()
    spell.execute()
    study_spell(spell)
    study_spell(Confundo())
    

Answer the following questions:

  • What are the parent and child classes here?
  • What does the code print out? (Try figuring it out without running it in Python)
  • Which get description method is called when ‘study spell(Confundo())’ is executed? Why?
  • What do we need to do so that ‘print Accio()’ will print the appropriate description (‘This charm summons an object to the caster, potentially over a significant distance’)? Write down the code that we need to add and/or change.
  1. Alyssa P. Hacker made the following Python class:

    class Address:
        def __init__(self, street, num):
            self.street_name = street
            self.number = num
    

She now wants to make a subclass of the class Address called CampusAddress that has a new attribute, office number, that can vary. This subclass will always have the street attribute set to Massachusetts Ave and the num attribute set to 77. She wants to use the class as follows:

>>> Sarina_addr = CampusAddress("32-G904")

>>> Sarina_addr.office_number
’32G-904’

>>> Sarina_addr.street_name
’Massachusetts Ave’

>>> Sarina_addr.number
77

Alyssa is stuck and needs your help. Please help her implement the CampusAddress class.