Python

ΕΝΟΤΗΤΑ 24 - Inheritance

image description

Στο σημερινό δωρεάν μάθημα Python, θα μιλήσουμε για την κληρονομικότητα (inheritance) που μπορεί να υπάρξει ανάμεσα στις κλάσεις και πως αυτή μπορεί να μας βοηθήσει στο να έχουμε ένα πιο οργανωμένο κώδικα. Ας δούμε όμως την έννοια της κληρονομικότητας μέσα από ένα παράδειγμα.

Στις προηγούμενες ενότητες, είχαμε ασχοληθεί με το απλό σενάριο να περιγράψουμε προγραμματιστικά την έννοια του εργαζόμενου. Αφού δημιουργήσαμε την κλάση με την οποία περιγράψαμε τον εργαζόμενο, προσθέσαμε μεταβλητές και μεθόδους για να μπορέσουμε να εκτελέσουμε κάποιες πράξεις με το συγκεκριμένο αντικείμενο.

Τώρα θέλουμε να μεγαλώσουμε την εφαρμογή μας και, εκτός από την γενική έννοια του εργαζόμενου, θέλουμε να καλύψουμε περιπτώσεις όπως τον μισθωτό και τον εξωτερικό συνεργάτη. Με όσα γνωρίζουμε μέχρι τώρα, λογικά θα πρέπει να δημιουργήσουμε δύο ακόμα κλάσεις με ονόματα SalaryEmployee και ExternalPartner αντίστοιχα. Ας δούμε και τις τρεις κλάσεις μαζί σε μια εφαρμογή. Για λόγους ευκολίας, θα χρησιμοποιήσουμε μια πιο απλή μορφή της κλάσης Employee.

App.py


class Employee():

    def __init__(self, id, name, salary, department):
        self.name = name
        self.id = id
        self.salary = salary
        self.department = department

    def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.salary}, department={self.department}'

class SalaryEmployee():
     def __init__(self, id, name, salary, department):
        self.name = name
        self.id = id
        self.salary = salary
        self.department = department

     def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.salary}, department={self.department}'

class ExternalPartner():
    def __init__(self, id, name, salary, department):
        self.name = name
        self.id = id
        self.salary = salary
        self.department = department

    def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.salary}, department={self.department}'

employee = Employee(100, 'Michail', 1000, 'IT')
print(employee)

salaryemployee = SalaryEmployee(200, 'George', 1200, 'Finance')
print(salaryemployee)

externalpartner = ExternalPartner(300, 'Maria', 1500, 'Marketing')
print(externalpartner)


         

Output


id=100, name=Michail, salary=1000, department=IT
id=200, name=George, salary=1200, department=Finance
id=300, name=Maria, salary=1500, department=Marketing	
	 

Αν και το πρόγραμμα μας δουλεύει, με μια γρήγορη ματιά παρατηρούμε ότι επαναλαμβάνουμε πολλαπλές φορές τον ίδιο κώδικα, όπως πχ. name και id. Το ιδανικό θα ήταν να κληρονομούσαμε όλα τα γενικά στοιχεία από μια κλάση, και εμείς απλά να προσθέταμε τα έξτρα στοιχεία που διαφοροποιούν την μια κλάση από μια άλλη. Στο δικό μας απλό παράδειγμα αυτός ο διαχωρισμός μπορεί να βασιστεί στον υπολογισμό του salary. Ο μισθωτός θα πληρώνεται ένα σταθερό ποσόν κάθε μήνα, ενώ ο εξωτερικός συνεργάτης θα πληρώνεται με βάση τις ώρες τις οποίες εργάστηκε σε ένα project. Πριν όμως φτάσουμε στο σημείο που θα υπολογίζουμε τον κάθε μισθό ξεχωριστά, πως μπορεί η κλάση SalaryEmployee να κληρονομήσει τα στοιχεία της Employee?

Ο τρόπος με τον οποίο ενεργοποιούμε την κληρονομικότητα ανάμεσα σε δύο κλάσεις είναι να προσθέσουμε μέσα στην παρένθεση της δεύτερης κλάσης το όνομα της κλάσης από την οποία επιθυμεί να κληρονομήσει.

App.py


class Employee():

    def __init__(self, id, name, department):
        self.name = name
        self.id = id
        self.department = department

    def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.salary}, department={self.department}'

class SalaryEmployee(Employee):
     def __init__(self, monthlysalary):
        self.monthlysalary = monthlysalary

     def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.monthlysalary}, department={self.department}'

class ExternalPartner(Employee):
    def __init__(self, hourlysalary):
        self.hourlysalary = hourlysalary

    def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.hourlysalary}, department={self.department}'

salaryEmployee = SalaryEmployee(1000)
salaryEmployee.id = 100
salaryEmployee.department = 'Marketing'
salaryEmployee.name = 'Michail'

print(salaryEmployee)

         

Output


id=100, name=Michail, salary=1000, department=Marketing	
	 

Στο παραπάνω παράδειγμα, η κλάση SalaryEmployee κληρονόμησε όλα τα χαρακτηριστικά της κλάσης Employee και απλά πρόσθεσε την έξτρα μεταβλητή monthlysalary που χρειαζόταν. Με αυτό τον τρόπο αποφύγαμε να επαναλάβουμε τον κώδικα της Employee για ακόμα μια φορά. Με την ίδια ακριβώς λογική θα μπορούσαμε να δημιουργήσουμε και ένα αντικείμενο από την κλάση ExternalPartner.

Η κλάση Employee ονομάζεται superclass, ενώ η SalaryEmployee και η ExternalPartner ονομάζονται subclasses. Φυσικά εκτός από attributes, οι subclasses κληρονομούν και τις μεθόδους από τις superclasses.

Ενώ ήδη καταφέραμε να μειώσουμε σημαντικά τον κώδικα μας, ο τρόπος που δημιουργήσαμε το αντικείμενο ίσως να μην μας ικανοποιεί και τόσο. Μετά την δημιουργία του αντικειμένου, χρειάστηκε να δώσουμε τιμές σε όλες τις μεταβλητές μια προς μια. Το καλύτερο, θα ήταν να μπορούσαμε να δώσουμε τις αρχικές τιμές που θα περιέχει το αντικείμενο κατά την διάρκεια της δημιουργίας του. Ακριβώς όπως ήδη γνωρίζουμε από τις προηγούμενες ενότητες. Αλλά πως όμως αυτό είναι εφικτό όταν δημιουργούμε ένα αντικείμενο με τις μεταβλητές του να έχουν κληρονομηθεί από άλλη κλάση?

Εδώ θα χρειαστούμε την βοήθεια της super() μεθόδου. Με την χρήση της super( ) μπορούμε να δημιουργήσουμε αντικείμενο στην subclass και να καλέσουμε τον constructor της superclass για να μας κάνει initialize το αντικείμενο με κάποιες τιμές. Ας δούμε πρώτα πως θα αλλάζει ο κώδικας μας όταν χρησιμοποιούμε την super( ), και μετά θα εξηγήσουμε λίγο περισσότερο την λειτουργία της.

App.py


class Employee():

    def __init__(self, id, name, department):
        self.name = name
        self.id = id
        self.department = department

    def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.salary}, department={self.department}'

class SalaryEmployee(Employee):
     def __init__(self, id, name, department, monthlysalary):
        self.monthlysalary = monthlysalary
        super().__init__(id, name, department)

     def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.monthlysalary}, department={self.department}'

class ExternalPartner(Employee):
    def __init__(self, id, name, department, hourlysalary):
        self.hourlysalary = hourlysalary
        super().__init__(id, name, department)

    def __str__(self):
        return f'id={self.id}, name={self.name}, salary={self.hourlysalary}, department={self.department}'

salaryEmployee = SalaryEmployee(100, 'Michail', 'IT', 1000)
print(salaryEmployee)

         

Output


id=100, name=Michail, salary=1000, department=IT
	 

Ας εξηγήσουμε λοιπόν τι καταφέραμε με τον παραπάνω κώδικα. Η κλάση SalaryEmployee απαιτεί να έχει διαθέσιμες τις τιμές για τις μεταβλητές id, name, department και monthlysalary πριν από την δημιουργία του αντικειμένου γιατί πολύ απλά θα τις αναθέσει σε αυτό όταν το δημιουργήσει. Όμως μέσα στην κλάση SalaryEmployee, εκτός από την monthlysalary, δεν έχουμε ορίσει καμία άλλη μεταβλητή. Όταν λάβουμε λοιπόν τις τιμές για τις τρεις μεταβλητές, με την χρήση της μεθόδου super(), καλούμε τον constructor της κλάσης Employee. Αυτός αναλαμβάνει να θέσει τις τιμές για τις μεταβλητές που γνωρίζει στο αντικείμενο. Λόγω της κληρονομικότητας λαμβάνουμε λοιπόν αυτό το initialization έτοιμο από την κλάση Employee. Σαν τελευταίο βήμα, προσθέτουμε την monthlysalary για να δημιουργήσουμε την τελική μορφή του αντικειμένου.

ΕΙΣΗΓΗΤΗΣ

Merry Jhonson

Μιχάλης Κασάπογλου

Ο Μιχάλης Κασάπογλου, είναι ένας από τους πιο έμπειρους τεχνικούς εκπαιδευτές στον χώρο του προγραμματισμού με πάνω από 20 χρόνια εμπειρία. Έχει εργαστεί σαν IT Operations Manager, Senior Programmer, και Training Team Leader ενώ κατέχει και αρκετές πιστοποιήσεις που καλύπτουν ένα μεγάλο φάσμα τεχνολογιών στο προγραμματισμό, σε βάσεις δεδομένων και cloud. Στον ελεύθερο του χρόνο διατηρεί ένα τεχνολογικό blog στο οποίο θα βρείτε αρκετά δωρεάν μαθήματα προγραμματισμού για αρχάριους.

loader