היי,
בדוגמא הבאה: למה בפונקציית __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
.
ים, שווה אולי להוסיף את התגובה שלך (בצורה ובניסוח מסויימים) למחברת, כי היא מסבירה את הנקודה הזאת באופן הכי נהיר שעד עכשיו קראתי. פשוט ראיתי שהיו עוד שאלות בפורומים על זה (גם שלי :)). סתם נקודה למחשבה.
אז אם אני מבינה נכון, אם אני רוצה לשנות פונקציה אצל יורש שמוכרזים בה משתנים שהם לא self.משתנה,
אני חייבת להעתיק אותם שוב?
או שלא, אם את לא עושה בהם שימוש.
או לא להעתיק, אם את רוצה שיהיה להם ערך אחר.
או להפוך את המשתנים במחלקת־העל לכן self
(אם זה הגיוני שהמשתנים האלו יהיו מוגדרים כתכונות של המופע).
יש הרבה אפשרויות.
כשאת דורסת פונקציה בעיקרון את אומרת “לא, רגע, אני צריכה מימוש אחר כאן”.
מה את עושה מהשלב הזה זה להחלטתך ולהנדסתך, לפי הצרכים שלך ומה שנראה לך שנכון הנדסית.
תודה על ההסבר, ברגע שאני מתחילה לדרוס פונקציה אז כל הפונקציה הראשונית (שאותה אני עורכת) בעצם מתה? אני צריכה להגדיר הכל מחדש אצל היורש?
ממליץ לשחק עם זה קצת, התשובות לשאלה הזו מופיעות במחברת בהרחבה.
יש לי 2 שאלות נוספות…
בדוגמא הנ"ל -
- במחלקת EmailClient בפעולה read למה נדרש לכתוב self.provider? ולא provider? הרי אם אני מספקת לו מופע חיצוני למה אני צריכה את ה self?
- במחלקות walla ו gmail למה אין את הפונקציה init?
-
אנחנו מספקים מופע אבל המופע הזה מאוחסן בארגומנט בתוך האובייקט של EmailClient ממש כמו השמה רגילה לצורך העניין ולכן יש שימוש בself כדי להצביע לו באיזה ארגומנט לאחסן את האובייקט הזה.
-
יש פונקציה init , היא פשוט הפונקציה init של המחלקה object של פייתון שכולם יורשים ממנה. כפי שהוסבר במחברת, כל מחלקה שאנו יוצרים ולמעשה כמעט כל דבר שאנחנו יוצרים בפייתון הוא אובייקט של מחלקה שנקראת object. לכן גם זו נחשבת תכנות שהיא Object Oriented. או OOP
מה שאנחנו עושים שאנחנו עושים init במחלקות שלנו זה בעצם “דורסים” את הinit המקורית ומכניסים חדשה.
ברגע שלא הכנסנו בעצם אנו “מסמכימים” שהיא תהיה כמו בצורת הדיפולט שלה במחלקת האם object.
לגבי 1 אני עדיין לא ממש מבינה…
נראה לי התכוונת ל self.provider = provider שעם זה אני מסכימה.
מה שלא הבנתי זה את ה self שמודגש בצהוב
ברגע שמחלקה יורשת ממחלקה אחרת, היא מקבלת את התכונות שלה (כל עוד לא הגדרנו init ייחודי).
לכן self.provider.
מה שסימנת זה בתוך הפונקציה read של המחלקה EmailClient יש שם רצון להגיע לפונקציה read שנמצאת בתוך המופע של provider.
אז כדי להגיע לשם תחילה ניגשים למופע שמאוחסן בארגומנט פרווידר:
self.provider
ולאחר מכן ניגשים אל הפונקציה שבתוך המופע הזה על ידי .read
self.provider.read()
הפונקציה עצמה מקבלת את ה self.username כלומר את הארגומנט username של המחלקה EmailClient
הכל יחד יוצא:
self.provider.read(self.username)