fbpx

01. מחלקות – מבוא

הקדמה

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

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

בשיעור זה, אנו נלמד על מחלקות. באנגלית – Classes.

מהי מחלקה?

מחלקה היא תבנית קוד, שמיצגת לוגיקה כלשהי. מהתבנית הזו ניתן ליצור אובייקטים.

בואו נדמה זאת למכולת.

ובכן, בואו נחשוב שאנו כותבים תוכנה, שמטרתה היא לנהל מוצרים במכולת.
ברור לנו כמנהלי המכולת, שלמרות שישנם הרבה מוצרים, עדיין לכל מוצר יש:

  • שם המוצר
  • ברקוד
  • עלות (למוכר)
  • מחיר (לצרכן)
  • תאריך ייצור
    וכן הלאה…

בשביל לכתוב תוכנה שיהיה לנו קל לכתוב ולעבוד איתה, אנו נרצה להמציא איזושהי תבנית שתייצג מוצר.

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

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

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

כאשר בעל המכולת ירצה להכניס מוצר חדש למכולת, הוא יוכל להשתמש בתבנית (במחלקה) בצורה קלה.

בואו נראה איך עושים זאת.

מחלקה – הגדרה:

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

בואו נצלול לכתיבה, ותוך כדי נבין טוב יותר.

הבה נגדיר מחלקה בשם מוצר.

הגדרת המחלקה תהיה כך:

				
					class Product():
     name = None
				
			

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

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

נוכל לדמיין את מה שעשינו כגיליון אקסל בשם product, שציינו בו “עמודה” בשם name. עדיין אין לנו שורות כלל.

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

היצירה תיעשה באופן שמזכיר קריאה לפונקציה… אנו ניצור משתנה חדש, לדוגמא X, שיכיל “עותק” או יותר נכון “מופע” של המחלקה. באופן הבא:

				
					x = Product() 

				
			

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

				
					x.name = "Sugar"
				
			

כעת X הוא מופע של המחלקה “מוצר”, וגם יש לו שם – סוכר. (שורה באקסל, שתחת העמודה name ציינו “סוכר”)
אם נרצה להדפיס את “שם המוצר” של המופע X, נעשה זאת כך:

				
					print(x.name) 
				
			

כשהגדרנו את המחלקה, יצרנו רק שדה אחד בשם name. כמובן שבדרך כלל יהיו לנו יותר משדה בודד…

בשביל לעשות זאת, אנו נוכל ליצור עוד משתנים. כך:

				
					class Product():
     name = None
     barcode = None
     price = None 
				
			

כעת נוכל ליצור מופעים שונים של המחלקה “מוצר”, ולציין את המאפיינים שלהם:

				
					sugar = Product()
sugar.name = "Sugar"
sugar.barkod = "345824"
sugar.price = 1.5

salt = Product()
salt.name = "Salt"
salt.barkod = "930043"
salt.price = 4.99 
				
			

כעת יש לנו שני מוצרים (מופעים), שניהם “שייכים” לאותה מחלקה “מוצר”, אך לכל אחד נתונים שונים בשדות המאפיינים אותם.

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

תרגיל:

צור מחלקה בשם person אשר מייצגת “אדם”. בשביל לייצג ‘אדם’ אנו נרצה שיהיו לו את השדות הבאים:

  •  שם
  •  מגדר
  •  גיל
  •  תעודת זהות

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

הפתרון בסרטון:

אנו נוכל להגדיר פונקציה שתהיה פונקציה של המחלקה.
פונקציה של מחלקה, זו “פעולה” ששייכת למחלקה.

מה זאת אומרת?

נמשיך עם הדוגמה של מחלקה המייצגת מוצר.
נניח שאנו רוצים ליצור פונקציה, אשר מחזירה את המחיר של המוצר בתוספת המע”מ.
ברור שמה שעל הפונקציה לעשות זה לקחת את המחיר של המוצר מתוך השדה price, ולהכפיל אותו ב 1.18 (בהנחה שהמע”מ הוא 18%)
בעיקרון פונקציה כזו אמורה לדעת לעבוד אך ורק בעולמם של ה”מוצרים”, ולכן במקום להגדיר סתם פונקציה כללית, נוכל להגדיר את הפונקציה בצורה כזו שהיא תהיה שייכת למחלקה.

איך עושים זאת?

אז בקוד הבא יצרנו שוב את המחלקה Product, עם השדות “שם” ו”מחיר”, אלא שהפעם הוספנו למחלקה גם “פונקציה” משלה:

				
					class Product():
     name = None
     price = None
     def plus_maam(self):
        print("Calculating Taxes..")
        result = self.price * 1.18   # Ma'am = 18%
        return result 
				
			

נשים לב מה עשינו:

  • בתוך השורות המוזחות של המחלקה, יצרנו פונקציה בצורה הרגילה שאנו מכירים, בעזרת המילה השמורה def לאחריה ציון השם – קראנו לה בשם “פלוס-מעמ”.
  • אח”כ הוספנו סוגריים עגולות ונקודותיים, כמו בכל פונקציה.
  • החלק המוזר הוא: בתוך הסוגריים העגולות הגדרנו משתנה בשם self.
    המשתנה self שציינו, הוא יהיה ה “מופע” שעליו הפונקציה תבצע את החישוב.
    ואכן בשורות לאחר מכן, בשביל לחשב את המע”מ, הפונקציה פנתה דרך המופע self לשדה price.

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

אז נניח שיש לנו מוצר (מופע) בשם salt, ואנו רוצים לדעת מה מחירו כולל מע”מ, אנו לכאורה נצטרך לקרוא לפונקציה, ולשלוח לה את המופע בעל השם salt (שיכנס לתוך ה-self)

				
					salt = Product()
salt.name = "Salt"
salt.price = 4.99
final_price = plus_maam(salt) 
				
			

אך לא, אם נעשה זאת אנו נקבל שגיאה.
הסיבה היא שהפונקציה plus_maam לא מוכרת למחשב. בשביל שהיא תהיה מוכרת למחשב, היא חייבת להופיע בהקשר של המחלקה.
כיוון שיש לנו ‘מופע’ של המחלקה, אנו נוכל באמצעות המופע salt לגשת לפונקציה:

				
					final_price = salt.plus_maam(salt) 
				
			

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

אך לא, גם צורה זו איננה לגמרי תקינה, למה?
כיוון שאנו ניגשים לפונקציה דרך המשתנה salt, אנו לא צריכים יותר לשלוח את המשתנה salt בצורה מפורשת לפונקציה. הוא יעבור לשם אוטומטית 🙂
ואם כך, כל מה שנצטרך לעשות זה פשוט:

				
					final_price = salt.plus_maam() 
				
			

כעת בתוך הפונקציה, המשתנה self יקבל את המופע salt. אך בצורה מרומזת.

מה הכוונה בצורה מרומזת?

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

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

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

				
					def plus_maam(self, maam):
     return self.price * (1 + maam) 
				
			

כעת, כאשר נשתמש במופע salt כדי לחשב את מחירו כולל המע”מ, נצטרך לשלוח לפונקציה רק את הערך של המע”מ, כי המשתנה עצמו כאמור, נשלח בצורה מרומזת.
אז בקיצור, נעשה כך:

				
					final_price = salt.plus_maam(0.18) 
				
			

הערה: אולי יהיה חכם מצידנו להגדיר ערך ברירת מחדל עבור המע”מ כמו שלמדנו בעולם הפונקציות:

				
					def plus_maam(self, maam=0.18):
     . . . 
				
			
תרגיל: Box

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

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

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

הפתרון בסרטון:

בנאי

פונקצית איתחול – בנאי (באנגלית: Constructor)

כאשר יצרנו אובייקט (או ‘מופע’) של מחלקה, ונתנו ערכים לשדות שלו, עשינו זאת בכמה שורות.
שורת יצירת האובייקט:

				
					salt = Product() 
				
			
ואז שורת הצבת ערך:
				
					salt.name = "Salt" 
				
			

למעשה, ישנה דרך קצרה ונעימה יותר, שתאפשר לנו לקבוע את שם המוצר כבר בעת יצירתו. 
באופן הבא:

				
					salt = Product(name="Salt") 
				
			

מה עשינו?
בתוך הסוגריים העגולות שאנו כותבים בעת יצירת מופע, הוספנו שם של שדה וערך.

נכון דרך מגניבה?
נוכל גם “לאתחל” כמה שדות, למשל:

				
					salt = Product(name="Salt", price=5.5) 
				
			

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

				
					class Product():
     def __init__(self, name, price):
        self.name = name
        self.price = price
        self.barcod = None 
				
			

נשים לב לכמה דברים:

  • שם הפונקציה הוא קצת מוזר, והוא חייב להיות כך: __init__
  • הפונקציה מקבלת את המופע self, וגם את הפרמטרים שנרצה לאפשר “לאתחל” בעת יצירת המופע.
  • שמות המשתנים שהתקבלו לפונקציה הם name ו-price, אבל כמובן שיכולנו סתם לקרוא להם x ו-y. רק שאם היינו עושים כך, שורת היצירה של המופע לא תוכל להיות יותר:
				
					salt = Product(name="Salt", price=5.5) 
				
			
אלא
				
					salt = Product(x="Salt", y=5.5) 
				
			

אני מקווה שברור לכם למה, אם לא, חזרו לפרק של הפונקציות 🙂

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

תרגיל:

צור מחלקה המייצגת שולחן.
עליך ליצור לשולחן את השדות:

  • אורך
  • רוחב
  • מחיר

השולחנות שהמחלקה מייצגת, כולם מתוצרת איקאה, כך שלמחלקה גם יהיה שדה בשם company שיאותחל תמיד בשם IKEA.

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

בנוסף, למחלקה תהיינה הפונקציות הבאות:

  • פונקצית חישוב שטח השולחן (רוחב * אורך)
  • פונקציה בשם is_worth, המקבלת מספר כלשהו X, ויודעת להדפיס האם “כדאי” לקנות שולחן זה או לא.
    הבדיקה תהיה כך: אם המחיר חלקי השטח של השולחן גדול מ X (הפרמטר שהתקבל לפונקציה) אז התשובה תהיה “אמת”, אחרת – False.

לסיום, צור קוד היוצר שני שולחנות, אחד עם מחיר “כדאי”, ואחד עם מחיר “לא כדאי”. (כאשר ה X לבדיקת כדאיות שווה 50)
השתמש בפונקציות של המחלקה בשביל להראות את הכדאיות…

פתרון:
				
					class Table():
    def __init__(self, length, width, price=100):  # בנאי
        self.company = 'IKEA'
        self.length = length
        self.width = width
        self.price = price
    def get_size(self):
        return self.length * self.width
    def is_worth(self, x):
        size = self.get_size()
        piece_price = self.price / size
        return piece_price < x   # return a Boolean value

# Testing class code:
table1 = Table(3, 2, 10)
table2 = Table(3, 2, 8000)
print(table1.is_worth(50))
print(table2.is_worth(50)) 
				
			

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

ולא על ידי קו תחתון (כמו פונקציות או משתנים) My_class.

ואם במנהגים עסקינן…
גם השימוש שלנו במילה self בשביל לציין את שם המשתנה המכיל את המופע, הוא מנהג… יכולנו להשתמש בשם אחר לכך.

אם אנו מתעניינים ואוהבים את ה “מנהגים” הללו, אנו נרצה להציץ במסמך הזה.

אעיר ואוסיף, שהמנהגים הללו הם אחד ההבדלים בין מתכנת מקצועי לשאינו כזה…

חבר’ה, יש לי עוד הרבה דברים לספר לכם על מחלקות.
יש מלא דברים שאפשר לעשות עם מחלקות שלא דיברנו עליהם, אני אנסה להכיר לכם את העיקריים שבהם שלא ציינו.

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

כניסה

שכחתי סיסמה

אין לך חשבון? להצטרפות

איפוס סיסמה:

שכחת את הסיסמה? יש להזין את שם המשתמש או כתובת האימייל. הוראות איפוס הסיסמה ישלחו באימייל.

ברכות! סיימתם את השיעור הראשון בקורס!

עשיתם את הצעד הראשון בדרך שלכם להיות מתכנתים מקצועיים
הצטרפו אלינו כדי להמשיך במסלול שיוביל אתכם לעתיד בהייטק

שינוי סיסמה

עריכת פרטים אישיים