שאלת הבנה על דריסה

היי,
בדוגמא הבאה: למה בפונקציית __str__במחלקת Instrumental הגדירו שוב את הפרמטרים: artists, title , separator ?
הרי אין שום שינוי בהגדרה שלהם ממחלקת האב Song.
רק ה return שונה

היי,
זו שאלה טובה והתייחסו אליה כבר כמה פעמים פה בפורום.
שימי לב שלא מדובר במשתנים שקשורים למופע – זה לא self.artists, אלא פשוט artists.

למשתנים “רגילים” שלא מצומדים למופע יש משהו שנקרא “scope” – סביבה שבהם הם מוגדרים.
בדיוק כמו שבפונקציה רגילה משתנה יווצר בתוך הפונקציה ולא יהיה קיים יותר אחרי שנעשה return, כך גם כאן.

המשתנה artists (או title, או separator) שמוגדר בתוך Instrumental.__str__ שייך רק לפונקציה הזו.
קריאה ל־super().__str__() אכן תריץ את Song.__str__, ובאמת מוגדרים בה המשתנים artists, title ו־separator, אבל הם לא שייכים לפונקציה Instrumental.__str__ כי הם מוגדרים ב־scope אחר. אלו משתנים אחרים שיכולים להיות להם ערכים אחרים.

אפשר לחשוב על Instrumental.__str__ ועל Song.__str__ כשתי סביבות שונות לחלוטין.
מה שמשותף ביניהן נשמר על המופע עצמו, הרי הוא self.

2 לייקים

ים, שווה אולי להוסיף את התגובה שלך (בצורה ובניסוח מסויימים) למחברת, כי היא מסבירה את הנקודה הזאת באופן הכי נהיר שעד עכשיו קראתי. פשוט ראיתי שהיו עוד שאלות בפורומים על זה (גם שלי :)). סתם נקודה למחשבה.

3 לייקים

אז אם אני מבינה נכון, אם אני רוצה לשנות פונקציה אצל יורש שמוכרזים בה משתנים שהם לא self.משתנה,
אני חייבת להעתיק אותם שוב?

או שלא, אם את לא עושה בהם שימוש.
או לא להעתיק, אם את רוצה שיהיה להם ערך אחר.
או להפוך את המשתנים במחלקת־העל לכן self (אם זה הגיוני שהמשתנים האלו יהיו מוגדרים כתכונות של המופע).
יש הרבה אפשרויות.

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

תודה על ההסבר, ברגע שאני מתחילה לדרוס פונקציה אז כל הפונקציה הראשונית (שאותה אני עורכת) בעצם מתה? אני צריכה להגדיר הכל מחדש אצל היורש?

ממליץ לשחק עם זה קצת, התשובות לשאלה הזו מופיעות במחברת בהרחבה.

יש לי 2 שאלות נוספות…
בדוגמא הנ"ל -

  1. במחלקת EmailClient בפעולה read למה נדרש לכתוב self.provider? ולא provider? הרי אם אני מספקת לו מופע חיצוני למה אני צריכה את ה self?
  2. במחלקות walla ו gmail למה אין את הפונקציה init?

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

  2. יש פונקציה init , היא פשוט הפונקציה init של המחלקה object של פייתון שכולם יורשים ממנה. כפי שהוסבר במחברת, כל מחלקה שאנו יוצרים ולמעשה כמעט כל דבר שאנחנו יוצרים בפייתון הוא אובייקט של מחלקה שנקראת object. לכן גם זו נחשבת תכנות שהיא Object Oriented. או OOP
    מה שאנחנו עושים שאנחנו עושים init במחלקות שלנו זה בעצם “דורסים” את הinit המקורית ומכניסים חדשה.
    ברגע שלא הכנסנו בעצם אנו “מסמכימים” שהיא תהיה כמו בצורת הדיפולט שלה במחלקת האם object.

לגבי 1 אני עדיין לא ממש מבינה…
נראה לי התכוונת ל self.provider = provider שעם זה אני מסכימה.
מה שלא הבנתי זה את ה self שמודגש בצהוב

ברגע שמחלקה יורשת ממחלקה אחרת, היא מקבלת את התכונות שלה (כל עוד לא הגדרנו init ייחודי).
לכן self.provider.

לייק 1

מה שסימנת זה בתוך הפונקציה read של המחלקה EmailClient יש שם רצון להגיע לפונקציה read שנמצאת בתוך המופע של provider.

אז כדי להגיע לשם תחילה ניגשים למופע שמאוחסן בארגומנט פרווידר:

self.provider

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

self.provider.read()

הפונקציה עצמה מקבלת את ה self.username כלומר את הארגומנט username של המחלקה EmailClient
הכל יחד יוצא:

self.provider.read(self.username) 
4 לייקים