פייתון מונחה עצמים 3: הורשה

מחבר:
בתאריך:

אחד המאפיינים של תכנות מונחה עצמים הוא שימוש בהורשה - העברת תכונות ומתודות ממחלקה הורה super class למחלקות היורשות אותה sub class. הורשה מיעלת את כתיבת הקוד, שאותו נכתוב פעם אחת בהורה ואחר כך נשתמש בו במחלקות היורשות במקום לכתוב את אותו הקוד שוב ושוב בכל מחלקה בנפרד.

אנו יוצרים הורשה כאשר אנו מעבירים את שם המחלקה ההורה כפרמטר למחלקה היורשת.

כך נראה המקרה הכללי:

class Parent:
  pass

class Child(Parent):
  pass
  • העברנו את המחלקה Parent כפרמטר למחלקה Child כדי ש-Child תירש את Parent.

בדוגמה הבאה, המחלקה SportsCar יורשת את המחלקה Car, ועל כן יכולה להשתמש בתכונות והמתודות של המחלקה ההורה:

class Car:
  def __init__(self, model):
    self.model = model

  def say_hello(self):
    return "Hello, I'm a {}" . format(self.model)


class SportsCar(Car):
  pass
  • בדוגמה לעיל, המחלקה SportCar אינה כוללת תכונות ומתודות משל עצמה אבל היא יורשת את הקוד של המחלקה ההורה.

ניצור אובייקט חדש מהמחלקה SportsCar ונורה לו להגיד שלום באמצעות המתודה say_hello() אותה הוא ירש מההורה:

sportsCar1 = SportsCar('Audi R8')
print(sportsCar1.say_hello())

והתוצאה:

Hello, I'm a Audi R8

זה עבד :-). למרות ששם המודל model והמתודה say_hello() שייכים למחלקה ההורה, Car, הצלחנו לעבוד איתם מהמחלקה SportsCar בזכות הירושה.

הודות לשימוש בירושה הצלחנו לכתוב את הקוד רק פעם אחת בהורה ולהשתמש בו במחלקה היורשת. המשמעות היא מניעת קוד שחוזר על עצמו בהתאם לאחד העקרונות התכנותיים החשובים ביותר עיקרון ה DRY - Don't Repeat Yourself - אל תחזור על עצמך שאומר שקוד צריך לכתוב פעם אחת בלבד כדי לחסוך את הצורך לערוך אותו במספר מקומות שונים כשצריך לעדכן את התוכנה. במדריכים באינטרנט הקוד פשוט וקצת קשה להבין למה העיקרון הוא כל כך חשוב אבל בחיים כשיש לך כמות עצומה של קוד לנהל דבקות בעיקרון "אל תחזור על עצמך" יכולה לחסוך לך הרבה זמן ומבוכה. אז תעשה לעצמך טובה על תחזור על עצמך כשאתה כותב קוד כשאחד האמצעים לכך הוא שימוש בהורשה.

בזכות הירושה הצלחנו לכתוב את הקוד רק פעם אחת ולהשתמש בו במחלקה היורשת. המשמעות היא מניעת קוד שחוזר על עצמו בהתאם לאחד העקרונות התכנותיים החשובים ביותר "אל תחזור על עצמך".

 

ניתן להוסיף למחלקה היורשת מתודות משלה

המחלקה היורשת, כמו כל מחלקה רגילה, יכולה להכיל מתודות משל עצמה.

נוסיף למחלקה SportsCar את המתודה over_drive():

class Car:
  def __init__(self, model):
    self.model = model

  def say_hello(self):
    return "Hello, I'm a {}" . format(self.model)


class SportsCar(Car):
  def over_drive(self):
    return "Smashing it!!"

נקרא למתודות של הקלאס SportsCar:

sportsCar1 = SportsCar('Audi R8')
print(sportsCar1.say_hello())
print(sportsCar1.over_drive())

והתוצאה:

Hello, I'm a Audi R8
Smashing it!!

אחת השגיאות שתלמידים עושים היא לנסות לקרוא לתכונות של המחלקה היורשת מתוך ההורה. אל תעשו את השגיאה הזו!! כי ירושה בתכנות כמו בחיים עוברת מההורים לצאצאים ולא להיפך.

ירושה בתכנות כמו בחיים עוברת מההורים לצאצאים ולא להיפך.

 

דריסה של מתודות ע"י המחלקה היורשת

המחלקה היורשת יכולה "לדרוס" מתודה שהיא ירשה מההורה אם נגדיר במחלקה היורשת מתודה בעלת שם זהה. פרקטיקה המכונה method overriding. נשתמש בדריסה אם נצטרך שהמתודה תתפקד באופן שונה בקלאס היורש.

בדוגמה הבאה, נדרוס את say_hello() אותה ירשנו מהמחלקה Car בתוך המחלקה היורשת, SportsCar:

class Car:
  def __init__(self, model):
    self.model = model

  def say_hello(self):
    return "Hello, I'm a {}" . format(self.model)


class SportsCar(Car):
  def over_drive(self):
    return "Smashing it!!"

  def say_hello(self):
    return "Hello, I'm a {} and I'm {}" . format(self.model, self.over_drive())
    
sportsCar1 = SportsCar('Audi R8')
print(sportsCar1.say_hello())

התוצאה:

Hello, I'm a Audi R8 and I'm Smashing it!!

המתודה say_hello() מתנהגת לפי הקוד שכתבנו במחלקה היורשת בגלל שדרסנו את קוד המתודה שירשנו מההורה.

 

ניתן להוסיף למחלקה היורשת תכונות משלה

המחלקה היורשת יכולה לכלול תכונות משל עצמה אותם נגדיר בתוך המתודה המיוחדת __init__().

בדוגמה הבאה ננסה להגדיר בתוך המחלקה היורשת את התכונה sports_engine:

class Car:
  def __init__(self, model):
    self.model = model

  def say_hello(self):
    return "Hello, I'm a {}" . format(self.model)


class SportsCar(Car):
  def __init__(self, sports_engine):
    self.sports_engine = sports_engine

  def over_drive(self):
    return "Smashing it!! With my {} engine".format(self.sports_engine)

sportsCar1 = SportsCar('V8')
print(sportsCar1.say_hello())
print(sportsCar1.over_drive())

והתוצאה:

Traceback (most recent call last):
  File "oop3.py", line 17, in 
    print(sportsCar1.say_hello())
  File "oop3.py", line 6, in say_hello
    return "Hello, I'm a {}" . format(self.model)
AttributeError: 'SportsCar' object has no attribute 'model'

יש לנו בעיה! כי כדי שהאובייקט יהיה שייך למחלקה Car הוא צריך שיוגדר עבורו model במחלקה ההורה וגם sports_engine במחלקה היורשת.

מה עושים?

הפתרון הוא להשתמש בפונקציה super() בתוך הקונסטרקטור __init__() של המחלקה היורשת כדי להעביר למחלקה ההורה את התכונות ההכרחיות ליצירת האובייקט.

בדוגמה הבאה נעביר לקונסטרקטור __init__() של המחלקה SportsCar את שתי התכונות:

  • model ההכרחית למחלקה ההורה
  • sports_engine בשביל המחלקה היורשת
class Car:
  def __init__(self, model):
    self.model = model

  def say_hello(self):
    return "Hello, I'm a {}" . format(self.model)


class SportsCar(Car):
  def __init__(self, model, sports_engine):
    super().__init__(model)
    self.sports_engine = sports_engine

  def over_drive(self):
    return "Smashing it!! With my {} engine".format(self.sports_engine)

בתוך הקונסטרקטור __init__() של המחלקה היורשת השתמשנו ב-

super().__init__(model)

כדי להעביר להורה את התכונה הדרושה לו.

נעביר לאובייקט את שתי התכונות בעת יצירתו:

sportsCar1 = SportsCar('Audi', 'V8')

נקרא למתודה say_hello() שמקורה בהורה:

print(sportsCar1.say_hello())
Hello, I'm an Audi

נקרא למתודה over_drive של היורש:

print(sportsCar1.over_drive())
Smashing it!! With my V8 engine

לכל המדריכים בסדרה ללימוד פייתון

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

הוסף תגובה חדשה

 

= 8 + 9

תמונת המגיב

חדוה בתאריך: 23.09.2020

בדוגמא על method overriding יש טעות כי זה אמור להדפיס "Hello, I'm a Audi R8 and I'm Smashing it!!"
זה לא מדפיס את זה בגלל ההזחה של המתודה say_hello במחלקה היורשת (צריך שהיא תהיה בתוך הscope של הclass)

תמונת המגיב

יוסי בן הרוש בתאריך: 24.09.2020

תודה, חדוה, תקנתי.