class Song:
    """Represent a Song in our lyrics site.

    Parameters
    ----------
    name : str
        The name of the song.
    lyrics : str
        The lyrics of the song.
    artists : list of str or str, optional
        Can be either a list, or a string separated by commas.

    Attributes
    ----------
    name : str
        The name of the song.
    lyrics : str
        The lyrics of the song.
    _views : int
        Views counter, which indicates how many times the users printed
        a specific song.
    _artists : list of str
        A list of the song's artists.
    """
    def __init__(self, name, lyrics, artists=None):
        self.name = name
        self.lyrics = lyrics
        self._views = 0
        self._artists = self._reformat_artists(artists)

    def _reformat_artists(self, artists):
        if isinstance(artists, str):
            return self._listify_artists_from_string(artists)
        elif artists is None:
            return []
        return artists

    def _listify_artists_from_string(self, artists):
        """Create list of artists from string."""
        for possible_split_token in (', ', ','):
            if possible_split_token in artists:
                return artists.split(possible_split_token)
        return [artists]

    def add_artist(self, artist):
        """Add an artist to the song's artists list."""
        self._artists.append(artist)

    def remove_artist(self, artist):
        """Remove an artist from the song's artists list."""
        if len(self._artists) <= 1 or artist not in self._artists:
            return False
        self._artists.remove(artist)

    def get_artists(self):
        """Return the song's artists list."""
        return self._artists
    
    def count_words(self):
        """Return the word count in the song's lyrics."""
        return len(self.lyrics.split())

    def __str__(self):
        self._views = self._views + 1
        artists = ', '.join(self.get_artists())
        title = f'"{self.name}" / {artists}'
        separator = "-" * len(title)
        return (
            f"{title}\n"
            + f"{separator}\n"
            + f"{self.lyrics}\n"
            + f"{separator}\n"
            + f"Seen: {self._views} time(s)."
        )


lyrics = """
Her Majesty's a pretty nice girl
But she doesn't have a lot to say
Her Majesty's a pretty nice girl
But she changes from day to day

I want to tell her that I love her a lot
But I gotta get a bellyful of wine
Her Majesty's a pretty nice girl
Someday I'm going to make her mine, oh yeah
Someday I'm going to make her mine
""".strip()
her_majestys = Song("Her Majesty's", lyrics, "The Beatles")
print(her_majestys)
print(her_majestys.count_words())

"""
Explanation: <img src="images/logo.jpg" style="display: block; margin-left: auto; margin-right: auto;" alt="לוגו של מיזם לימוד הפייתון. נחש מצויר בצבעי צהוב וכחול, הנע בין האותיות של שם הקורס: לומדים פייתון. הסלוגן המופיע מעל לשם הקורס הוא מיזם חינמי ללימוד תכנות בעברית.">
<span style="text-align: right; direction: rtl; float: right;">ירושה</span>
<span style="text-align: right; direction: rtl; float: right; clear: both;">הקדמה</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    בשבוע שעבר למדנו מה הן מחלקות, וסקרנו טכניקות שונות הנוגעות לכתיבת קוד בעזרתן.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    למדנו כיצד ליצור <em>מחלקה</em>, שהיא בעצם תבנית שמתארת את ה<em>תכונות</em> ואת ה<em>פעולות</em> השייכות לכל <em>מופע</em> שנוצר ממנה.<br>
    הסברנו מהי <em>פעולת אתחול</em> (<code>__init__</code>), שרצה מייד עם יצירתו של <em>מופע</em> חדש, ודיברנו על <em>פעולות קסם</em> נוספות.<br>
    ראינו כיצד מגדירים <em>משתני מחלקה</em>, כאלו שמשותפים לכל המופעים,<br>
    ודיברנו גם על <em>תכונות ופעולות פרטיות ומוגנות</em>, טכניקה שמאפשרת לנו להחליט אילו תכונות ופעולות אנחנו חושפים למשתמשים שלנו.<br>
    לסיום, דיברנו גם על רעיון ה<em>הכלה</em>, שמאפשר לנו להשתמש במופעי מחלקה אחת בתוך מופעים של מחלקה אחרת. 
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    השבוע נרחיב את ארגז הכלים שלנו, ונדבר על רעיונות נוספים שקשורים לעולם המחלקות.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">אתר השירים</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    השבוע נתמקד ביצירת אתר השירים החדש והנוצץ "שירומת".<br>
    נתחיל בכתיבת מחלקה המייצגת שיר עבור האתר.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    לכל שיר יש שם, מילים ורשימת יוצרים.<br>
    כדי לנהל את רשימת היוצרים, צרו את הפעולות <var>add_artist</var> ו־<var>remove_artist</var> שיוסיפו ויסירו אומנים בהתאמה.<br>
    לשיר תהיה גם פעולה שנקראת <var>count_words</var> שתחזיר את מספר המילים בשיר.<br>
    כמו כן, לכל שיר יהיה מונה שיספור כמה פעמים הוא הודפס.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    הדפסת שיר תיראה כך:
</p>

<div class="align-center" style="display: flex; text-align: right; direction: rtl; clear: both;">
    <div style="display: flex; width: 10%; float: right; clear: both;">
        <img src="images/exercise.svg" style="height: 50px !important;" alt="תרגול"> 
    </div>
    <div style="width: 70%">
        <p style="text-align: right; direction: rtl; float: right; clear: both;">
            נסו לממש את מחלקת השיר בעצמכם. 
        </p>
    </div>
    <div style="display: flex; width: 20%; border-right: 0.1rem solid #A5A5A5; padding: 1rem 2rem;">
        <p style="text-align: center; direction: rtl; justify-content: center; align-items: center; clear: both;">
            <strong>חשוב!</strong><br>
            פתרו לפני שתמשיכו!
        </p>
    </div>
</div>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    נציג את הפתרון, ונסביר את המימוש מייד לאחר מכן:
</p>
End of explanation
"""


class Acrostic(Song):
    pass

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    פעולת ה־<code dir="ltr">__init__</code> של המחלקה <code>Song</code> מגדירה את השם ואת הליריקה של השיר.<br>
    היא גם מאתחלת את מספר הצפיות בשיר ל־0, ויוצרת את רשימת האומנים של השיר.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כדי להקל על המשתמש במחלקה, אפשרנו להעביר את שמות האומנים כרשימה, כמחרוזת מופרדת בפסיקים, או לא להכניס אומנים כלל.<br>
    עיבוד השמות לכדי צורה אחידה, רשימה של מחרוזות, יתבצע בפעולה <code dir="ltr">_reformat_artists</code>, שתחליט כיצד לפעול לפי סוג המשתנה:<br>
    אם הועברה לפרמטר האומנים מחרוזת, נשלח אותה ל־<code dir="ltr">_listify_artists_from_string</code> שתיצור מהם רשימה. אם הועבר <code>None</code>, נחזיר רשימה ריקה.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    הפעולה <var>count_words</var> מפצלת את מילות השיר לרשימה, ומחזירה את מספר האיברים ברשימה.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    בקריאה ל־<code>__str__</code> אנחנו מגדילים את ערכה של התכונה <var dir="ltr">_views</var> ב־1. כך נספור את הפעמים שביקשו להדפיס שיר. 
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">אקרוסטיכון</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    אקרוסטיכון, כזכור לנו משבועות קודמים, הוא שיר ששרשור האות הראשונה של כל שורה בו יוצר ביטוי קריא.<br> 
    כחלק מהטכנולוגיה המשוכללת של "שירומת", נרצה ליצור מחלקה שמייצגת אקרוסטיכון.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כשחושבים על זה, אקרוסטיכון הוא סוג של שיר, כך שתכונות המחלקה ופעולות המחלקה אמורות להיות זהות לאלו של <var>Song</var>.<br>
    למעשה, בבואנו לבנות את מחלקת האקרוסטיכון <var>Acrostic</var> נגלה במהרה שהיא העתק מדויק כמעט של מחלקת <var>Song</var> שכבר כתבנו.<br>
    חסרה לנו רק הפעולה <var>get_acrostic</var>, נניח, שתחזיר לנו את האקרוסטיכון שנמצא בשיר.<br>
    בהינתן המצב הזה, איך יהיה נכון לכתוב את מחלקת אקרוסטיכון?
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    הפתרון הראשון שקופץ לראש הוא פשוט להוסיף למחלקה <var>Song</var> את הפעולה "<var>get_acrostic</var>".<br>
    זה, כמובן, לא מתאים כל כך.<br>
    ברוב השירים אין אקרוסטיכון, והפעולה הזו לא מתאימה להם ולא שייכת למחלקה הכללית יותר, <var>Song</var>.<br>
    הוספת הפעולה למחלקה <var>Song</var> גם תיצור עומס מיותר במחלקה – מה יקרה כשנרצה לתמוך בחמשירים? או בשירים מעגליים?<br>
    עד מהרה כל מופע יכלול פעולות רבות שלא קשורות אליו, והשימוש במחלקה יהפוך מסורבל מאוד.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    הפתרון השני, אם כך, הוא להעתיק את הקוד של מחלקת <var>Song</var> וליצור מחלקה כמעט זהה בשם <var>Acrostic</var>.<br>
    במחלקה יהיו בדיוק כל התכונות והפעולות שנמצאות תחת <var>Song</var>, וכן את הפעולה "<var>get_acrostic</var>".<br>
    במקרה הזה מדובר בשכפול קוד – כך שנראה שאף פתרון זה אינו מושלם.<br>
    בכל פעם שנרצה לשנות משהו במחלקה <var>Song</var> נצטרך לשנות את שתי המחלקות,<br>
    ועד מהרה פעולת התחזוקה של הקוד הכפול תהפוך להיות מפרכת, לא נעימה ומקור לבאגים נוראיים.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    נסכם: אנחנו מחפשים מנגנון או טכניקה שיאפשרו לנו לשכפל את התכונות והפעולות של מחלקה אחת למחלקה אחרת.<br>
    השכפול צריך להשאיר את המחלקה המקורית עובדת ועצמאית, וליצור מחלקה חדשה עם תכונות ופעולות שזהות לאלו של המחלקה המקורית.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">הטכניקה</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    הפתרון לבעיה שתיארנו בהקדמה נקרא "<dfn>ירושה</dfn>".<br>
    נגדיר מחלקת <var>Acrostic</var> ריקה, שיורשת ממחלקת <var>Song</var>.<br>
    כשאנחנו אומרים דבר כזה, אנחנו מתכוונים שהמחלקה הריקה <var>Acrostic</var> תקבל את כל התכונות והפעולות שנמצאות בתוך מחלקת <var>Song</var>.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">התחביר</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    התחביר של ירושה הוא פשוט:<br>
    בסוף השורה שבה אנחנו מגדירים את המחלקה וקובעים את שמה, נוסיף סוגריים שבהם נציין את שם המחלקה שממנה אנחנו רוצים לרשת.
</p>
End of explanation
"""


lyrics = """A boat, beneath a sunny sky
Lingering onward dreamily
In an evening of July -
Children three that nestle near,
Eager eye and willing ear,

Pleased a simple tale to hear -
Long has paled that sunny sky:
Echoes fade and memories die:
Autumn frosts have slain July.
Still she haunts me, phantomwise,
Alice moving under skies
Never seen by waking eyes.
Children yet, the tale to hear,
Eager eye and willing ear,

Lovingly shall nestle near.
In a Wonderland they lie,
Dreaming as the days go by,
Dreaming as the summers die:
Ever drifting down the stream -
Lingering in the golden gleam -
Life, what is it but a dream?"""

song = Acrostic("A Boat, Beneath a Sunny Sky", lyrics, "Lewis Carroll")
print(song)
print(song.count_words())

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    וכמו קסם, בעזרת שורה אחת פשוטה שכפלנו את התכונות והפעולות של מחלקת <var>Song</var>.<br>
    ניצור מופע חדש של אקרוסטיכון בדיוק באותה הצורה שבה יצרנו מופע של שיר, רק שהפעם נשתמש במחלקת <var>Acrostic</var> במקום במחלקת <var>Song</var>.<br>
    זה אפשרי כיוון ש־<var>Acrostic</var> ירשה את פעולת <code>__init__</code> של <var>Song</var>. 
</p>
End of explanation
"""


help(Acrostic)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בדוגמה שלמעלה ראינו כיצד יוצרים מופע חדש של שיר בעזרת המחלקה <var>Acrostic</var>.<br>
    כשמחלקה יורשת ממחלקה אחרת, אפשר להתייחס למחלקה היורשת כאילו כל התכונות של <dfn>מחלקת־העל</dfn> (<dfn>superclass</dfn>), המחלקה שממנה היא יורשת, הועתקו אליה.<br>
</p>

<figure>
    <img src="images/inheritance.svg?v=3" style="max-width: 500px; margin-right: auto; margin-left: auto; text-align: center;" alt="בתמונה יש שתי תיבות. השמאלית עם הכותרת Song, ובה התכונות והפעולות השייכות למחלקה. אליה יוצא חץ מהתיבה הימנית, עם הכותרת Acrostic, ובה מופיעות אותן תכונות ופעולות בצבע אפור (כדי להמחיש שהן נלקחות מתוך המחלקה Song)."/>
    <figcaption style="margin-top: 2rem; text-align: center; direction: rtl;">המחלקה <var>Acrostic</var> יורשת מהמחלקה <var>Song</var>.<br>פנייה לפעולה או לתכונה של המחלקה <var>Acrostic</var> הריקה, תפנה את הבקשה למחלקת־העל <var>Song</var> שממנה <var>Acrostic</var> יורשת.</figcaption>
</figure>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כיוון ש־<var>Acrostic</var> ירשה את כל הפעולות ממחלקת־העל שלה, <var>Song</var>, ובהן גם את הפעולה <code dir="ltr">__init__</code>,<br>
    יצירת מופע חדש באמצעות קריאה ל־<var>Acrostic</var> קוראת למעשה לפעולה <code dir="ltr">Song.__init__</code>.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    פייתון לא מסתפקת בזה, ומעתיקה עבורנו גם את התיעוד של מחלקת־העל ושל הפעולות שבה:
</p>
End of explanation
"""


class Acrostic(Song):
    def get_acrostic(self):
        song_lines = self.lyrics.splitlines()
        first_chars = (line[0] for line in song_lines if line)
        return ''.join(first_chars)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    ירושה מאפשרת לנו להוסיף בקלות פעולות ל־<var>Acrostic</var>, מעבר לאלו שהיא ירשה ממחלקת <var>Song</var>.<br>
    כדי לעשות זאת, פשוט נגדיר את הפעולה הרלוונטית במחלקה היורשת.<br>
    נממש את הפעולה שלשמה התחלנו את כל העניין, <var>get_acrostic</var>, שתשרשר את האות הראשונה מכל שורה בשיר:
</p>
End of explanation
"""


song = Acrostic("A Boat, Beneath a Sunny Sky", lyrics, "Lewis Carroll")
song.get_acrostic()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    הפעולה <var>get_acrostic</var> אוספת את האות הראשונה מכל שורה, אם קיימת כזו, ומאחדת את כל האותיות שנאספו למחרוזת אחת:
</p>
End of explanation
"""


song = Song("A Boat, Beneath a Sunny Sky", lyrics, "Lewis Carroll")
song.get_acrostic()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    שימו לב - אומנם הפעולה קיימת במחלקה <var>Acrostic</var>, אך אין זה אומר שהיא קיימת במחלקה <var>Song</var>:
</p>
End of explanation
"""


song = Acrostic("A Boat, Beneath a Sunny Sky", lyrics, "Lewis Carroll")
print(isinstance(song, Song))
print(isinstance(song, Acrostic))

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    נוכל לראות שפייתון מבינה שמופע שנוצר מ־<var>Acrostic</var> הוא גם <var>Acrostic</var>, אבל הוא גם <var>Song</var>:
</p>
End of explanation
"""


song = Song("A Boat, Beneath a Sunny Sky", lyrics, "Lewis Carroll")
print(isinstance(song, Song))
print(isinstance(song, Acrostic))

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    אבל לא להפך:
</p>
End of explanation
"""


class Instrumental(Song):
    pass

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    מכאן שירושה היא חד־כיוונית: המחלקה היורשת מקבלת את כל התכונות והפעולות של מחלקת־העל, אבל לא להפך.<br>
    אם מחלקה א יורשת ממחלקה ב, מופע שנוצר ממחלקה א יכול להשתמש בתכונות ובפעולות שמוגדרות במחלקה ב.<br>
    למרות זאת, במקרה שכזה, מופע שנוצר ממחלקה ב לא יוכל להשתמש בתכונות ובפעולות שמוגדרות במחלקה א.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כשמשתמשים בירושה, נהוג שתת־המחלקה היורשת יכולה לגשת ולשנות גם תכונות פרטיות של מחלקת־העל שאותה היא יורשת.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">תרגיל ביניים: ססטי... מה?</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
לפי <a href="https://he.wikipedia.org/wiki/%D7%A1%D7%A1%D7%98%D7%99%D7%A0%D7%94">ויקיפדיה</a>, ססטינה הוא שיר בעל מבנה נוקשה שמציית לכללים הבאים:
</p>

<ul style="text-align: right; direction: rtl; float: right; clear: both;">
    <li>בססטינה יש 39 שורות: שישה בתים של שש שורות כל אחד, ולבסוף בית של שלוש שורות.</li>
    <li>המילה האחרונה בכל שורה בבית הראשון, מופיעה גם כמילה האחרונה בכל אחת מהשורות מהבתים הבאים אחריה. אם נסמן את שש המילים האחרונות בשש השורות של הבית הראשון באותיות אבגדהו, הרי הסדר שבו יופיעו מילים אלה בבתים הבאים הוא ואהבדג, גודאבה, הגבואד, דהאגוב, בדוהגא.</li>
    <li>שש מילים אלה מופיעות גם בבית האחרון, שבו שלוש שורות. בשורה הראשונה יופיעו המילים בה, בשורה השנייה – דג, ובשורה השלישית – או.</li>
</ul>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    ממשו את המחלקה <var>Sestina</var>.<br>
    המחלקה תכיל את כל התכונות והפעולות של שיר רגיל, נוסף על הפעולה <var>is_valid</var> שתבדוק אם הסטטינה תקינה.<br>
    בבדיקתכם, התעלמו מהחוקים הנוגעים לבית האחרון.<br>
    הפעולה תחזיר <code><samp>True</samp></code> אם כן ו־<code><samp>False</samp></code> אם לא.<br>
    בדקו את הפעולה שלכם על "<a href="https://www.firstpost.com/blogs/neil-gaimans-ode-to-vampires-27212.html">ססטינת ערפד</a>" של ניל גיימן.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">דריסה</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    לפעמים נרצה לרשת ממחלקה מסוימת, אבל חלק מהפעולות או מהתכונות במחלקת־העל לא יתאימו לצרכים שלנו.<br>
    במקרים אלו כן נרצה לרשת ממחלקת־העל את תכונותיה ופעולותיה, אבל נגדיר מחדש תכונות ופעולות שאנחנו רוצים לשנות. 
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    ניקח כדוגמה את מחלקת <var>Instrumental</var>.<br>
    <dfn>קטע כלי</dfn> (או שיר אינסטרומנטלי) הוא קטע מוזיקלי ללא שירה.<br> 
</p>
End of explanation
"""


song = Instrumental("River Flows in You", "", "Yiruma")
print(song)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    ניצור מתוך המחלקה מופע עבור הקטע של Yiruma, יצירתו המהממת River Flows in You:
</p>
End of explanation
"""


class Instrumental(Song):
    def __str__(self):
        self._views = self._views + 1
        artists = ', '.join(self.get_artists())
        title = f'"{self.name}" / {artists}'
        separator = "-" * len(title)
        return f"{title}\n{separator}\nSeen: {self._views} time(s)."

song = Instrumental("River Flows in You", "", "Yiruma")
print(song)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בשלב הזה נגלה שפעולת ה־<code dir="ltr">__str__</code> עושה פחות חסד עם קטעים כליים.<br>
    תעלול נחמד בירושה הוא היכולת לדרוס את הפעולה של מחלקת־העל בתת־מחלקה שיורשת ממנה.<br>
    בדריסה, אנחנו "מבטלים" את הפעולה של מחלקת־העל (היא לא תרוץ), ומחליפים אותה בפעולה חדשה שניצור עבור תת־המחלקה.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    נדרוס את הפעולה <code dir="ltr">__str__</code> ונממש צורת תצוגה שמתאימה יותר לקטעים כליים:
</p>
End of explanation
"""


Instrumental.mro()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בתא שלמעלה הגדרנו מחדש את הפעולה <code dir="ltr">__str__</code> שבמחלקה <var>Instrumental</var>.<br>
    ויתרנו על הצגת מילות השיר (כי אין כאלו) ועל הקווים המפרידים הסמוכים להם.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כמה נחמד!<br>
    עכשיו כשנדפיס את המופע, מי שתיקרא כדי להמיר את המופע למחרוזת היא הפעולה <code dir="ltr">Instrumental.__str__</code> ולא <code dir="ltr">Song.__str__</code>.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    איך פייתון מחליטה לאיזו פעולה לקרוא?<br>
    השיטה היא תמיד לחפש תחילה במחלקה שממנה נוצר המופע.<br>
    אם לא מצאנו את הפעולה שם, נעבור לחפש אותה במחלקת־העל.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    סדר המחלקות שבו נחפש את הפעולה קיבל את השם "סדר בחירת פעולות" (Method Resolution Order; או <abbr title="Method Resolution Order">MRO</abbr>).<br>
    אפשר לראות את סדר בחירת הפעולות של <var>song</var> אם נקרא ל־<code dir="ltr">Instrumental.mro()</code>:
</p>
End of explanation
"""


class Example:
    pass

e = Example()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    זה אומר שכשאנחנו יוצרים מופע מהמחלקה <var>Instrumental</var>, פייתון תחפש פעולות ותכונות קודם כול אצלה, ורק אז במחלקה <var>Song</var>, הרי היא מחלקת־העל.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    נשים לב שבראש השרשרת עומדת המחלקה <var>object</var>.<br>
    כשאנחנו מגדירים מחלקה "רגילה", ללא ירושה, פייתון אוטומטית מניחה שהיא יורשת ממחלקת <var>object</var>.<br>
    מחלקה זו מגדירה התנהגויות בסיסיות עבור המחלקה שיצרנו.<br>
    לדוגמה: הפעולה <code dir="ltr">__init__</code> שמוגדרת ב־<var>object</var> מאפשרת לנו לאתחל מופעים חדשים מבלי שנגדיר <code dir="ltr">__init__</code> במחלקה שלנו:<br>
</p>
End of explanation
"""


print(e)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    הפעולה <code dir="ltr">__str__</code> שמוגדרת ב־<var>object</var> מאפשרת לנו לראות את הייצוג המכוער של אובייקט כשאנחנו מדפיסים אותו,<br>
    גם בלי לממש את <code dir="ltr">__str__</code> במחלקה שלנו:
</p>
End of explanation
"""


song = Instrumental("Kiss the rain", "", "Yiruma")
print(song)

"""
Explanation: <span style="text-align: right; direction: rtl; float: right; clear: both;">super</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    מקום נוסף לשיפור במחלקה <var>Instrumental</var> הוא פעולת ה־<code dir="ltr">__init__</code>.<br>
    כיוון שלקטעים כליים אין מילים, הפרמטר השני שאנחנו מעבירים לפעולת האתחול (<var>lyrics</var>) הוא מיותר לחלוטין.
</p>
End of explanation
"""


class Instrumental(Song):
    def __init__(self, name, artists=None):
        self.name = name
        self.lyrics = ""
        self._views = 0
        self._artists = self._reformat_artists(artists)

    def __str__(self):
        self._views = self._views + 1
        artists = ', '.join(self.get_artists())
        title = f'"{self.name}" / {artists}'
        separator = "-" * len(title)
        return f"{title}\n{separator}\nSeen: {self._views} time(s)."

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    היה עדיף לו <code>__init__</code> של המחלקה <var>Instrumental</var> היה קצת שונה מה־<code>__init__</code> של המחלקה <var>Song</var>.<br>
    מה עושים? את הפעולה שהורשה דורסים!<br>
    נגדיר <code dir="ltr">__init__</code> חדש ללא הפרמטר <var>lyrics</var>:
</p>
End of explanation
"""


class Instrumental(Song):
    def __init__(self, name, artists=None):
        Song.__init__(self, name=name, lyrics="", artists=artists)

    def __str__(self):
        self._views = self._views + 1
        artists = ', '.join(self.get_artists())
        title = f'"{self.name}" / {artists}'
        separator = "-" * len(title)
        return f"{title}\n{separator}\nSeen: {self._views} time(s)."

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בקוד שלמעלה <dfn>דרסנו</dfn> את הפעולה <code dir="ltr">__init__</code>.<br>
    חדי העין מביניכם זיהו שהיא כמעט זהה לפעולת ה־<code dir="ltr">__init__</code> של <var>Song</var>.<br>
    ההבדלים הם שלא מוגדר בה הפרמטר <var>lyrics</var>, ושתכונת המופע <var>lyrics</var> מוגדרת תמיד למחרוזת ריקה. 
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    מצד אחד – השגנו את מה שרצינו. מצד שני – מטרת העל של ירושה הייתה מלכתחילה למנוע שכפול קוד.<br>
    נצמצם את כפילות הקוד בין <code dir="ltr">Instrumental.__init__</code> לבין <code dir="ltr">Song.__init__</code> –<br>
    במקום להעתיק את השורות שכתובות ב־<code dir="ltr">Song.__init__</code>, נקרא לפעולת האתחול הזו מתוך <code dir="ltr">Instrumental.__init__</code>:
</p>
End of explanation
"""


def __init__(self, name, lyrics, artists=None):
    self.name = name
    self.lyrics = lyrics
    self._views = 0
    self._artists = self._reformat_artists(artists)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    מה יקרה כעת בעת יצירת מופע חדש של <var>Instrumental</var>?<br>
</p>

<ol style="text-align: right; direction: rtl; float: right; clear: both;">
    <li>הפעולה <code dir="ltr">Instrumental.__init__</code> תרוץ.</li>
    <li>בשורה הראשונה של הפעולה, תיקרא הפעולה <code dir="ltr">Song.__init__</code> כאשר מועבר לה הפרמטר <var>self</var> באופן מפורש.</li>
    <li>כשהפעולה <code dir="ltr">Song.__init__</code> תרוץ, השורה <code dir="ltr">self.name = name</code> שנמצאת בתוך הפעולה תתייחס למופע שיצרנו ממחלקת <var>Instrumental</var>.<br>הפעולה מתנהגת כך כיוון שהעברנו לפרמטר <code>self</code> של הפעולה <code dir="ltr">Song.__init__</code> את המופע שיצרנו במחלקת <var>Instrumental</var>.</li>
</ol>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    עשינו פה טריק מוזר ואולי קצת מלוכלך.<br>
    פנינו ישירות לפעולת הקסם <code dir="ltr">Song.__init__</code>, ו"השתלנו" בה את ה־<var>self</var> שמייצג את המופע הנוכחי.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    למה עשינו זאת?<br>
    אם נקרא ל־<code dir="ltr">Song()</code>, ייווצר אוטומטית מופע חדש של שיר "רגיל", והוא יהיה זה שייכנס לפרמטר <var>self</var> של <code dir="ltr">Song.__init__</code>.<br>
    לעומת זאת, אם נקרא ישירות ל־<code dir="ltr">Song.__init__()</code>, נוכל להעביר את הפרמטר <var>self</var> באופן מפורש, בעצמנו.<br>
    הטריק הזה מאפשר לנו להעביר לתוך הפרמטר <var>self</var> של <code dir="ltr">Song.__init__</code> מופע שיצרנו בעזרת מחלקת <var>Instrumental</var>,<br>
    או במילים אחרות – הטריק הזה מאפשר לנו להפעיל את פעולת האתחול של <var>Song</var> עבור המופע שנוצר מ־<var>Instrumental</var>.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    למה זה שימושי?<br>
    כיוון שאז אנחנו מנצלים את פעולת האתחול של <var>Song</var> שנראית כך:
</p>
End of explanation
"""


class Instrumental(Song):
    def __init__(self, name, artists=None):
        Song.__init__(self, name=name, lyrics="", artists=artists)

    def __str__(self):
        Song.__str__(self)
        artists = ', '.join(self.get_artists())
        title = f'"{self.name}" / {artists}'
        separator = "-" * len(title)
        return f"{title}\n{separator}\nSeen: {self._views} time(s)."

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    באמצעות שורת הקוד שהוספנו ל־<var>Instrumental</var> שקוראת לפעולה הזאת, פעולת האתחול של <var>Song</var>,<br>
    אנחנו חוסכים את שכפול הקוד בתת־המחלקה וגורמים לכך שהפעולה של מחלקת־העל תפעל ותגדיר עבורנו את תכונות המחלקה.<br>
    זו דרך נוחה להגיד לפייתון "אמנם דרסנו את הפעולה של מחלקת־העל, אבל עדיין נרצה לבצע <em>גם</em> את מה שמחלקת־העל עושה".<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    זה עובד כיוון שהעברנו את <var>self</var>, המופע שלנו, לפעולה <code dir="ltr">Song.__init__</code>,<br>
    מה שיגרור את הגדרתן של <code>self.name</code>, <code>self.lyrics</code>, <code>self._views</code> ו־<code>self._artists</code> עבור המופע שנוצר מ־<var>Instrumental</var>.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    קריאה לפעולה שדרסנו במחלקת־העל היא טריק נפוץ ושימושי מאוד.<br>
    למעשה, נהוג להשתמש בו הרבה גם על פעולות שהן לא <code dir="ltr">__init__</code>.<br>
    נוכל להחיל את אותו הטריק גם על <code dir="ltr">__str__</code>, לדוגמה, ולחסוך את ההעלאה של <code dir="ltr">self._views</code> ב־1:
</p>

<div class="align-center" style="display: flex; text-align: right; direction: rtl; clear: both;">
    <div style="display: flex; width: 10%; float: right; clear: both;">
        <img src="images/exercise.svg" style="height: 50px !important;" alt="תרגול"> 
    </div>
    <div style="width: 70%">
        <p style="text-align: right; direction: rtl; float: right; clear: both;">
            ממשו בעצמכם את אותו תעלול עבור <code dir="ltr">__str__</code>.<br>
            הפתרון מופיע בתא שלהלן.
        </p>
    </div>
    <div style="display: flex; width: 20%; border-right: 0.1rem solid #A5A5A5; padding: 1rem 2rem;">
        <p style="text-align: center; direction: rtl; justify-content: center; align-items: center; clear: both;">
            <strong>חשוב!</strong><br>
            פתרו לפני שתמשיכו!
        </p>
    </div>
</div>
End of explanation
"""


class Instrumental(Song):
    def __init__(self, name, artists=None):
        super().__init__(name=name, lyrics="", artists=artists)

    def __str__(self):
        super().__str__()
        artists = ', '.join(self.get_artists())
        title = f'"{self.name}" / {artists}'
        separator = "-" * len(title)
        return f"{title}\n{separator}\nSeen: {self._views} time(s)."

song = Instrumental("River Flows in You", "Yiruma")
print(song)
print(song)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בתא שלמעלה אנחנו רואים איך משתמשים בפעולה של מחלקת־העל,<br>
    אבל עדיין מגדירים <em>גם</em> התנהגות עצמאית משל עצמנו.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כיוון שהטריק הזה נפוץ יחסית, פייתון נותנת בידינו דרך נוחה להתייחס למחלקת־העל, כשהפרמטר הראשון שנעביר לפעולה שבה הוא <var>self</var>.<br>
    במקום לכתוב בכל פעם את הביטוי המתיש <code dir="ltr">Song.__init__(self)</code>, נוכל לכתוב <code dir="ltr">super().__init__()</code>.<br>
    הפונקציה <var>super</var> מאפשרת לנו להעביר את המופע שיצרנו בתת־המחלקה, לתוך פעולה הנמצאת במחלקת־העל.
</p>
End of explanation
"""


class Clickable:
    def __init__(self):
        self.clicks = 0

    def click(self):
        self.clicks = self.clicks + 1

    def double_click(self):
        self.clicks = self.clicks + 2

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    חשוב להבין היטב את הרעיון של קריאה לפעולה במחלקת־העל מתוך הפעולה הדורסת בתת־מחלקה.<br>
    הטכניקה הזו מאפשרת לנו למנוע שכפול קוד, ואם בעתיד תשתנה מעט הפעולה במחלקת־העל, תת־המחלקה "תזכה" אוטומטית בשינויים האלו.<br>
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">תרגיל ביניים: דרדסים</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כידוע לכם בכפר הדרדסים יש הרבה דרדסים "רגילים", אבל יש גם כמה דרדסים מפורסמים, כמו דרדסבא, דרדסית ודרדשף.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    לכל דרדס (<var>Smurf</var>) יש את התכונה <var>name</var> שמכילה את שמו, ואת הפעולות <var>eat</var> ו־<var>sleep</var>.<br>
    לדרדס המיוחד "דרדסאבא" (<var>PapaSmurf</var>) יש גם את הפעולה <var>give_order</var>, שמקבלת פעולה של דרדס רגיל ומפעילה אותו עליו.<br>
    ל"דרדסית" (<var>Smurfette</var>) יש את הפעולה <var>kill_gargamel</var>, שמקבלת כפרמטר מופע שנוצר ממחלקת <var>Gargamel</var> ומשנה את התכונה <var>is_alive</var> שבו ל־<code>False</code>.<br>
    ל"דרדשף" (<var>ChefSmurf</var>) יש את הפעולה <var>create_food</var>, שמקבלת את שם המנה שהוא מכין וכמה "חתיכות" (<var>slices</var>) הוא יצר ממנה. 
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    דרדסים ישנים 8 שעות בדיוק. דרדס שישן לא יכול לבצע פעולות אחרות אלא אם הוא דרדסבא, שהוא חזק ומהיר והוא דרדס והוא גם חזק.<br>
    דרדס יכול לאכול מזון רק אם <var>ChefSmurf</var> הכין אותו באמצעות הפעולה <var>create_food</var>, ורק אם נשארו עוד "חתיכות" מאותה מנה.<br>
    הפעולה <var>eat</var> של כל דרדס תקבל רשימה של מנות ותאכל מכולן. הרשימה יכולה להכיל כל מספר איברים שהוא.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">ביקורת על ירושה</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    בשנות ה־90 העליזות החלה להישמע ביקורת הולכת וגוברת על רעיון הירושה.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
הביקורת העיקרית היא על כך שירושה יוצרת תלות בין מחלקת־העל לתתי־המחלקות שיורשות ממנה.<br>
כאשר מחלקה יורשת ממחלקה אחרת, כל הפעולות והתכונות של מחלקת־העל נחשפות לתת־המחלקה.<br>
נהוג שבתת־המחלקה אף מותר לשנות תכונות מוגנות של מחלקת־העל.<br>
התנהגות זו מובילה לצימוד גבוה מאוד בין מחלקות־העל לתתי־המחלקות, ופוגעת ברעיון הסתרת הנתונים שנמצא בליבת הרעיון של תכנות מונחה עצמים.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    סוג נוסף של ביקורת שמועברת תדיר על ירושה נוגעת לתסמונת בשם "מחלקת־העל השברירית".<br>
    תסמונת זו מדברת על כך שלאחר הירושה, שינוי במחלקת־העל עלול ליצור תקלים בקוד של תתי־המחלקות שיורשות ממנה.<br>
    נדון בבעיה זו בהרחבה בהמשך הפרק.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    שימוש רב במחלקות ויצירת היררכיית ירושה מורכבת ועמוסה עלולים גם לגרום ל"בעיית היו־יו" –<br>
    מתכנת שצריך לעבור על מחלקות בתוכנית רצוא ושוב כדי להבין את המטרה של חלק קטן בקוד.<br>
    היררכיה עמוסה שכזו גם מקשה על תחזוקת התוכנית וגורמת לניפוי שגיאות להיות קשה יותר במידה ניכרת.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    בספר האלמותי של כנופיית הארבעה, "תבניות עיצוב" (Design Patterns), הכותבים מותחים ביקורת נוקבת על הבעיות שירושה עלולה להכניס לקוד.<br>
    הם מציעים לבחור <a href="https://en.wikipedia.org/wiki/Composition_over_inheritance">בהכלה במקום בירושה</a> כשהדבר אפשרי.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">מחלקת העל השברירית</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    ירושה גורמת לקוד להיות חשוף לתסמונת "מחלקת־העל השברירית".<br>
    התסמונת מתארת בעיה נפוצה בירושה:<br>
    שינויים במחלקת־העל עלולים לגרור תוצאות בלתי צפויות בתתי־המחלקות שיורשות ממנה, אפילו אם מחלקת העל עובדת באופן מושלם.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    כדי לקבל מושג איך נראית התסמונת בעולם האמיתי, נראה דוגמה למימוש של אתר הקניות "מוצרי לכון ובניו".<br>
    נגדיר מחלקה פשוטה בשם <var>Clickable</var> שמתארת עצם לחיץ. המחלקה תאפשר לנו לספור כמה פעמים לחצו על מופע מסוים:
</p>
End of explanation
"""


class CrazyButton(Clickable):
    def click(self):
        self.double_click()

buy_now = CrazyButton()
buy_now.double_click()
buy_now.clicks

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    באתר הקניות המדובר ישנו מופע של כפתור שנוצר מהמחלקה <var>CrazyButton</var>.<br>
    המחלקה <var>CrazyButton</var> יורשת מהמחלקה <var>Clickable</var>.<br>
    המיוחד במחלקת <var>CrazyButton</var> הוא שכל לחיצה נחשבת כלחיצה כפולה:
</p>
End of explanation
"""


class Clickable:
    def __init__(self):
        self.clicks = 0

    def click(self):
        self.clicks = self.clicks + 1

    def double_click(self):
        self.click()
        self.click()


class CrazyButton(Clickable):
    def click(self):
        self.double_click()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    יום אחד, הסתכל אחיתופל המתכנת על הקוד. "רגע! יש פה קוד כפול!" הוא נזעק,<br>
    "הפעולה <var>double_click</var> במחלקה <var>Clickcable</var> עושה כמעט את מה שעושה הפעולה <var>click</var>".<br>
    אחיתופל ניגש בחופזה לתקן את הקוד, ולהשתמש ב־<var>click</var> פעמיים במקום בערך המפורש 2:
</p>
End of explanation
"""


buy_now = Clickable()
buy_now.click()
buy_now.double_click()
print(buy_now.clicks)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    ואכן, המחלקה <var>Clickable</var> עודנה עובדת מצוין: 
</p>
End of explanation
"""


buy_now = CrazyButton()
buy_now.click()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    אך מה יקרה אם נרצה להשתמש ב־<var>CrazyButton</var>?
</p>
End of explanation
"""


class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def get_area(self):
        return self.width * self.height

    def __str__(self):
        dimensions = f"{self.width}x{self.height}"
        return f"Size of {dimensions} is {self.get_area()}"


class Square(Rectangle):
    def __init__(self, side_size):
        super().__init__(side_size, side_size)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    אחיתופל אומנם תיקן את הקוד של המחלקה <var>Clickable</var>, שנראית עכשיו תקינה לחלוטין.<br>
    הבעיה היא שבתת־המחלקה <var>CrazyButton</var> הפעולה <var>click</var> מסתמכת על המימוש של הפעולה <var>double_click</var> שבמחלקת־העל שלה.<br>
    כך נוצרת שרשרת קריאות אין־סופית: בעת קריאה ל־<var>click</var> ב־<var>CrazyButton</var>, נקראת הפעולה <var>double_click</var>, שקוראת בחזרה לפעולה <var>click</var>, וחוזר חלילה.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">אז מתי כן ומתי לא?</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    בספר Code Complete מוצג כלל אצבע שזכה לאהדה בקרב מתכנתים רבים:<br>
    כשמתארים קשר בין שתי מחלקות בעזרת המילים "סוג של" (is a), הקשר התכנותי ביניהן יהיה לרוב ירושה.<br>
    כשמתארים קשר בין שתי מחלקות בעזרת המילה "יש..." (has a), הקשר התכנותי ביניהן יהיה לרוב הכלה.<br>
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    אם אנחנו יכולים להגיד "א הוא סוג של ב", כאשר מדובר בשמות של מחלקות, ייתכן שנכון להשתמש בירושה.<br>
    לדוגמה: כלב הוא סוג של חיה, ולכן ייתכן שמחלקת <var>Dog</var> תירש ממחלקת <var>Animal</var>.<br>
    מכונית היא סוג של רכב, ולכן הגיוני שמחלקת <var>Car</var> תירש ממחלקת <var>Vehicle</var>.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    אם אנחנו יכולים להגיד "אצל א יש ב", כאשר מדובר בשמות של מחלקות, ייתכן שנכון להשתמש בהכלה.<br>
    לדוגמה: למכונית יש מנוע, ולכן הגיוני שבמחלקה <var>Car</var> תופיע התכונה <var>engine</var>, שהיא מופע שנוצר מהמחלקה <var>Engine</var>.<br>
    זה עובד על תכונות באופן כללי, ולא רק על קשר של הכלה. לכפתור באתר יש כמה לחיצות, ולכן הגיוני ש־<var>clicks</var> תהיה תכונה של המחלקה <var>Button</var>.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">עקרון ההחלפה של ליסקוב</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    ברברה ליסקוב היא אחת מנשות מדעי המחשב המשפיעות בהיסטוריה, ואחת משלוש הנשים היחידות שזכו בפרס טיורינג.<br>
    תרומתה הנודעת ביותר נקראת "עקרון ההחלפה של ליסקוב" שנוסח פורמלית בשנת 1987.<br>
    בפשטות, העיקרון גורס כך:
</p>

<blockquote dir="rtl" style="direction: rtl; text-align: right; float: right; border-right: 5px solid rgba(0,0,0,.05); border-left: 0;">
    <p>כל מופע שנוצר ממחלקת־על מסוימת, חייב להמשיך לעבוד כרגיל גם אם ביום בהיר אחד יחליטו ליצור אותו מתת־מחלקה שיורשת מאותה מחלקת־על.</p>
</blockquote>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    נדמיין שיש לנו מחלקה של שיר ומחלקה של אקרוסטיכון שיורשת ממנה.<br>
    לפי ליסקוב, כל מופע שנוצר מהמחלקה "שיר" אמור להמשיך לפעול כרגיל, אפילו אם שינינו את הקוד כך שיווצר מהמחלקה "אקרוסטיכון". 
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    דוגמה פופולרית שנותן רוברט מרטין (הידוע בשם העט Uncle Bob) היא שכל ריבוע הוא גם מלבן.<br>
    חלקנו נמהר להשתמש בהורשה כדי לייצג את הרעיון הזה:
</p>
End of explanation
"""


print(Rectangle(5, 6))
print(Square(3))

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בקוד שלמעלה מימשנו מחלקת מלבן <var>Rectangle</var>, שבה התכונות "גובה" (<var>height</var>) ו"רוחב" (<var>width</var>), ואת הפעולה "חישוב שטח" (<var>get_area</var>).<br>
    בקוד ישנה מחלקה נוספת בשם ריבוע (<var>Square</var>) שפעולת האתחול שלה מקבלת רק אורך של צלע אחת, שכן כל צלעותיו של ריבוע זהות.<br>
    מחלקת הריבוע יורשת ממחלקת המלבן שבנינו.<br>
    הרעיון יעבוד נהדר:
</p>
End of explanation
"""


print(Rectangle(5, 6))

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    אבל שימו לב שאנחנו מפרים את עקרון ההחלפה של ליסקוב.<br>
    על פי העיקרון, כל שימוש במחלקה יוכל להיות מוחלף בתת־מחלקה שיורשת ממנה.<br>
    לפי אותו עיקרון, אנחנו אמורים להיות מסוגלים להחליף את הקריאה למחלקת מלבן בקריאה למחלקת ריבוע.<br>
    משמע, עבור:
</p>
End of explanation
"""


print(Square(5, 6))

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    אמור להתאפשר:
</p>
End of explanation
"""


my_square = Square(3)
my_square.width = 5
print(my_square)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    לְמַאי נַפְקָא מִנַּהּ? (יענו, ככה החלטתי לתכנת, למה מה תעשה לי?)
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    אז כמו שראינו – יצירת ריבוע שיגרתית כן תעבוד כמצופה.<br>
    עד שלפתע, אחד המתכנתים ירצה לשנות את רוחבו של הריבוע:
</p>
End of explanation
"""


class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height

    def set_width(self, width):
        self._width = width

    def set_height(self, height):
        self._height = height

    def get_width(self):
        return self._width

    def get_height(self):
        return self._height

    def get_area(self):
        return self.get_width() * self.get_height()

    def __str__(self):
        dimensions = f"{self.get_width()}x{self.get_height()}"
        return f"Size of {dimensions} is {self.get_area()}"


class Square(Rectangle):
    def __init__(self, side_size):
        super().__init__(side_size, side_size)

    def set_width(self, width):
        super().set_width(width)
        super().set_height(width)

    def set_height(self, height):
        super().set_width(height)
        super().set_height(height)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    לאחר עריכת רוחב הריבוע ציפינו ששטח הריבוע יהיה 25, אך ארכיטקטורת הקוד שלנו כשלה בטיפול במקרה הזה.<br>
    הקוד שלנו שינה רק את הרוחב של הריבוע ולא את אורכו, בזמן שבריבוע דבר כזה לא אמור להתאפשר.<br>
    בשורה התחתונה – הקוד שלנו נשבר כיוון שלא עקבנו אחרי עקרון ההחלפה.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    נוכל לתקן את הבאג בקלות יחסית אם נהפוך את <var>width</var> ואת <var>height</var> למשתנים פרטיים ב־<var>Rectangle</var>,<br>
        אבל כדאי לשים לב שבדרך כלל אם אנחנו פונים לתקן את מחלקת־העל כדי שדברים יעבדו – סימן שמשהו בעייתי בבסיס הארכיטקטורה שלנו.<br>
    ננסה בכל זאת:
</p>
End of explanation
"""


def set_and_print(my_shape):
    my_shape.set_height(2)
    my_shape.set_width(3)
    print(my_shape)


set_and_print(Rectangle(4, 5))
set_and_print(Square(4))

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    הדבר הראשון שקופץ לעין זה שיצרנו פה מפלצת. הקוד שלנו הפך מסורבל בניסיון לטפל בכל מקרי הקצה.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    הבעיה הבאה תצוץ כשנבנה פונקציה שאמורה לעבוד על מקרה כללי של מלבן או ריבוע:
</p>
End of explanation
"""


class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def get_area(self):
        return self.width * self.height

    def __str__(self):
        dimensions = f"{self.width}x{self.height}"
        return f"Size of {dimensions} rectangle is {self.get_area()}"


class Square:
    def __init__(self, side):
        self.side = side
    
    def get_area(self):
        return self.side ** 2

    def __str__(self):
        dimensions = f"{self.side}x{self.side}"
        return f"Size of {dimensions} square is {self.get_area()}"

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    המטרה הברורה של הפונקציה הייתה למצוא את השטח של הצורה לאחר שערכנו את גודלו ל־3 על 2.<br>
    מובן שהפונקציה לא החזירה את המצופה ממנה כשהעברנו לה ריבוע, כיוון שאי אפשר לשנות רק את רוחבו או רק את אורכו של הריבוע.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    בשלב הזה נפסיק לנסות לשכנע את הריבוע שהוא גם מלבן, ופשוט נממש את שתי המחלקות בנפרד: 
</p>
End of explanation
"""


class EmailClient:
    def __init__(self, username, password):
        print("Setting up a new mail client...")
        self._inbox = []
        self.username = username
        self.password = password

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    נזכה בקוד קצר, נוח לתחזוקה, שמאפשר למי שישתמש במחלקות שלנו לקבל בדיוק את מה שהוא רוצה בלי החשש שיטעה.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">העדיפו הכלה על ירושה כשאפשר</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    נממש תוכנה לקבלת דואר אלקטרוני, שיודעת לתמוך בקבלה ובשליחה של דואר מ־Gmail ומ־Walla – ספקי דואר אלקטרוני פופולריים.<br>
    נתחיל במימוש מחלקה כללית עבור התוכנה שלנו.<br>
    ההדפסות בפעולות הן לצורך ההמחשה. בדרך כלל נמליץ שלא לכלול הדפסות בתוך פעולות מחלקה או בתוך פונקציות.
</p>
End of explanation
"""


class Walla(EmailClient):
    DOMAIN = 'walla.co.il'

    def read(self):
        mail_address = f"{self.username}@{self.DOMAIN}"
        print(f"Reading mail of {mail_address} in Walla: [...]")


class Gmail(EmailClient):
    DOMAIN = 'gmail.com'

    def read(self):
        mail_address = f"{self.username}@{self.DOMAIN}"
        print(f"Reading mail of {mail_address} in Gmail: [...]")

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    כעת נממש את המחלקות של ספקי שירות הדואר האלקטרוני.<br>
    פתרון שעושה שימוש בירושה עשוי להיראות כך:
</p>
End of explanation
"""


mail = Walla(username='Yam', password='correcthorsebatterystaple')
mail.read()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    נדגים שימוש במחלקה:
</p>
End of explanation
"""


mail = Gmail(username='Yam', password='correcthorsebatterystaple')
mail.read()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    נשים לב שאם נרצה לעבור שירות דוא"ל, נהיה חייבים ליצור מופע חדש לחלוטין של תוכנת דוא"ל:
</p>
End of explanation
"""


class EmailClient:
    def __init__(self, username, password, provider):
        print("Setting up a new mail client...")
        self._inbox = []
        self.username = username
        self.password = password
        self.provider = provider
    
    def read(self):
        self.provider.read(self.username)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    מה יקרה אם נשתמש בהכלה במקום בירושה?<br>
    נתחיל בהתאמת המחלקה <var>EmailClient</var>:
</p>
End of explanation
"""


class Walla:
    DOMAIN = 'walla.co.il'

    def read(self, username):
        mail_address = f"{username}@{self.DOMAIN}"
        print(f"Reading mail of {mail_address} in Walla: [...]")


class Gmail:
    DOMAIN = 'gmail.com'

    def read(self, username):
        mail_address = f"{username}@{self.DOMAIN}"
        print(f"Reading mail of {mail_address} in Gmail: [...]")

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בקוד שלמעלה הוספנו את התכונה <var>provider</var> ל־<var>EmailClient</var>.<br>
    תכונה זו תכיל מופע של ספק הדוא"ל.<br>
    המחלקות של שירותי הדוא"ל יישארו כפי שהן, אך לא יירשו מ־<var>EmailClient</var>:
</p>
End of explanation
"""


mail = EmailClient(
    username='Yam',
    password='correcthorsebatterystaple',
    provider=Walla(),
)

mail.read()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    נראה דוגמה לשימוש:
</p>
End of explanation
"""


mail.provider = Gmail()
mail.read()

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    בניגוד לדוגמה שהשתמשה בירושה, אם נרצה להחליף ספק דוא"ל לא נצטרך ליצור מופע חדש של תוכנת דואר אלקטרוני.<br>
    פשוט נחליף את הספק:
</p>
End of explanation
"""


class Product:
    def __init__(self, product_id, name, price):
        self.id = product_id
        self.name = name
        self.price = price

    def __str__(self):
        return f"{self.name} - {self.price}$"

"""
Explanation: <span style="text-align: right; direction: rtl; float: right; clear: both;">סיכום</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    במחברת זו סקרנו את הרעיון של ירושה בתכנות מונחה עצמים.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    למדנו מהי ירושה ואיך משתמשים בה בפייתון,<br>
    ראינו איך דורסים פעולות ותכונות בתת־המחלקה, ואיך משתמשים ב־<var>super</var> כדי להתייחס למחלקת־העל.<br>
    סקרנו גם את הבעיות שמתעוררות כשמשתמשים בירושה, ולמדנו שיש לנהוג משנה זהירות לפני שבוחרים לממש משהו בעזרת מחלקות.<br>
    דיברנו על רעיונות תיאורטיים, כמו העדפת הכלה על ירושה, על סינדרום מחלקת־העל השברירית ועל עקרון ההחלפה של ליסקוב.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
    מתכנתים טובים משתמשים בירושה במשורה, רק אחרי שווידאו שבחירה בפתרון הזה לא תוסיף סיבוכיות מיותרת לקוד.<br>
    במחברת הבאה נסקור טכניקות פופולריות שנולדו בזכות רעיון הירושה, ונלמד כיצד משתמשים בהן.
</p>

<span style="text-align: right; direction: rtl; float: right; clear: both;">מונחים</span>
<dl style="text-align: right; direction: rtl; float: right; clear: both;">
    <dt>ירושה (Inheritance)</dt>
    <dd>
        מנגנון של שפת התכנות שמאפשר ביסוס של מחלקה א על תכונותיה ופעולותיה של מחלקה ב.
    </dd>
    <dt>מחלקת־על (Superclass)</dt>
    <dd>
        המחלקה שממנה מתבצעת הירושה. זו המחלקה שממנה ייגזרו הפעולות והתכונות, ו"יועתקו" למחלקה היורשת.<br>
        נקראת גם <dfn>מחלקת בסיס</dfn> (<dfn>base class</dfn>), <dfn>מחלקת האם</dfn> או <dfn>מחלקת האב</dfn> (<dfn>parent class</dfn>).
    </dd>
    <dt>תת־מחלקה (Subclass)</dt>
    <dd>
        המחלקה שמעתיקה את תכונותיה של מחלקה אחרת. זו המחלקה שאליה ייגזרו הפעולות והתכונות של המחלקה שממנה מתבצעת הירושה.<br>
        נקראת גם <dfn>המחלקה הנגזרת</dfn> (<dfn>derived class</dfn>) או <dfn>מחלקת הבת</dfn> (<dfn>child class</dfn>).
    </dd>
    <dt>דריסה (Override)</dt>
    <dd>
        החלפת תכונה או פעולה של מחלקת־העל בתת־מחלקה על ידי הגדרתה מחדש בתת־המחלקה.
    </dd>
    <dt>מחלקת־העל השברירית (Fragile base class)</dt>
    <dd>
        תסמונת המתארת כיצד שינוי במחלקת־העל באופן שמסתמן כתקין עבור מחלקת־העל, עלול לשבור את תתי־המחלקות שיורשות ממנה.
    </dd>
    <dt>עקרון ההחלפה של ליסקוב (Liskov Substitution Principle)</dt>
    <dd>
        עיקרון שמציע שעבור כל קוד שעושה שימוש במחלקת־על, יהיה אפשר להחליף את ההתייחסות למחלקת־העל בתת־המחלקות היורשות ממנה.<br>
        לפי העיקרון, אם ההחלפה אינה אפשרית, יש לשקול מחדש את קשרי הירושה בקוד.
    </dd>
    <dt>הכלה במקום ירושה (Composition over inheritance)</dt>
    <dd>
        רעיון תכנותי תיאורטי שמציע לשקול שימוש בהכלה במקום בירושה כשהדבר אפשרי.
    </dd>
</dl>

<span style="text-align: right; direction: rtl; float: right; clear: both;">קוד לדוגמה</span>
<p style="text-align: right; direction: rtl; float: right; clear: both;">
    לאדון פסו בגז' יש חנות גדולה לממכר ספרים.<br>
    ניצור מחלקה בסיסית שמייצגת מוצר בחנות של פסו:
</p>
End of explanation
"""


import datetime


class PhoneOrder:
    PHONE_CALL_TOLL_IN_USD = 1.99
    DELIVERY_PRICE_IN_USD = 5
    VAT_IN_PERCENTS = 20
    

    def __init__(self, seller_id, buyer_id, products):
        self.seller = seller_id
        self.buyer = buyer_id
        self.products = products
        self.time = datetime.datetime.now()
        self.price = self.calculate_price()
        self.delivered = False

    def calculate_price(self):
        base_price = sum(product.price for product in self.products)
        return base_price + self._calculate_extra_price(base_price)
    
    def _calculate_extra_price(self, base_price, include_vat=True):
        tax = self.VAT_IN_PERCENTS / 100
        return (
            base_price * tax
            + self.DELIVERY_PRICE_IN_USD
            + self.PHONE_CALL_TOLL_IN_USD
        )

    def __str__(self):
        return (
            f"Buyer #{self.buyer}, created by #{self.seller}.\n"
            + f"Delivered: {self.delivered}.\n"
            + f"{'-' * 40}\n"
            + "\n".join(str(product) for product in self.products)
            + f"\n{'-' * 40}\n"
            + f"Total: {self.price}$"
        )


book1 = Product(1, "The Fountainhead", 27.99)
book2 = Product(2, "Thinking, Fast and Slow", 19.69)
order = PhoneOrder(251, 666, [book1, book2])
print(order)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    הלקוח יכול לבצע הזמנה דרך המרשתת, דרך הגעה לאחד הסניפים או טלפונית.<br>
    נממש את המחלקה שאחראית להזמנה הטלפונית:
</p>
End of explanation
"""


import datetime


class Order:
    DELIVERY_PRICE_IN_USD = 5
    VAT_IN_PERCENTS = 20

    def __init__(self, seller_id, buyer_id, products):
        self.seller = seller_id
        self.buyer = buyer_id
        self.products = products
        self.time = datetime.datetime.now()
        self.price = self.calculate_price()
        self.delivered = False

    def calculate_price(self):
        base_price = sum(product.price for product in self.products)
        return base_price + self._calculate_extra_price(base_price)

    def _calculate_extra_price(
            self, base_price, include_vat=True, include_delivery=True,
    ):
        tax = self.VAT_IN_PERCENTS / 100
        price = base_price * tax
        if include_delivery:
            price = price + self.DELIVERY_PRICE_IN_USD
        return price

    def __str__(self):
        return (
            f"Buyer #{self.buyer}, created by #{self.seller}.\n"
            + f"Delivered: {self.delivered}.\n"
            + f"{'-' * 40}\n"
            + "\n".join(str(product) for product in self.products)
            + f"\n{'-' * 40}\n"
            + f"Total: {self.price}$"
        )


class PhoneOrder(Order):
    PHONE_CALL_TOLL_IN_USD = 1.99

    def _calculate_extra_price(self, base_price, **kwargs):
        base_price = super()._calculate_extra_price(base_price, **kwargs)
        return base_price + self.PHONE_CALL_TOLL_IN_USD


class OnlineOrder(Order):
    pass


class StoreOrder(Order):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.delivered = True

    def _calculate_extra_price(self, base_price, **kwargs):
        return super()._calculate_extra_price(
            base_price, include_delivery=False, **kwargs,
        )

print("Show all three kinds of orders:\n\n")

book1 = Product(1, "The Fountainhead", 27.99)
book2 = Product(2, "Thinking, Fast and Slow", 19.69)
order = PhoneOrder(seller_id=251, buyer_id=666, products=[book1, book2])
print(order)

print('\n\n')
order = StoreOrder(seller_id=251, buyer_id=666, products=[book1, book2])
print(order)

print('\n\n')
order = OnlineOrder(seller_id=251, buyer_id=666, products=[book1, book2])
print(order)

"""
Explanation: <p style="text-align: right; direction: rtl; float: right; clear: both;">
    דמיינו שכעת עליכם לממש את המחלקות שאחראיות להזמנות מהסניפים ומהמרשתת.
</p>

<p style="text-align: right; direction: rtl; float: right; clear: both;">
        עד מהרה נגלה שהקוד במחלקות הללו דומה מאוד לקוד שיצרנו במחלקת <var>PhoneOrder</var>.<br>
    אם כך, נשמע שהדבר הנכון הוא ליצור מחלקה ששלוש המחלקות יורשות ממנה:
</p>
End of explanation
"""


