היי חברים, ברוכים הבאים לקורס פייתון למתקדמים
ההנחה היא שאנחנו כבר יודעים איך קוד נראה, מהם משתנים, מערכים, לולאות, תנאים, ואפילו פונקציות. בהחלט הספקנו הרבה.
בקורס זה, אנו ניקח את כל הידע שצברנו, ונלמד להשתמש בו עם כלים נוספים.
בשיעור זה, אנו נלמד על מחלקות. באנגלית – 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
נשים לב מה עשינו:
אוקי, זה טוב ויפה.
איך כעת נוכל להשתמש בפונקציה הזו?
אז בשביל להשתמש בפונקציה של המחלקה, אנו חייבים שיהיה לנו קודם כל מופע שלה.
אז נניח שיש לנו מוצר (מופע) בשם 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):
. . .
צור מחלקה המייצגת “תיבה”.
המאפיינים של תיבה הם המידות שלה…
ולכן, תצטרך ליצור למחלקה שדות בשם: גובה, רוחב ואורך.
כמו כן דאג שלמחלקה תהיינה הפונקציות הבאות:
א. פונקציה המחשבת (ומחזירה) את השטח של הקוביה (רוחב כפול אורך)
ב. פונקציה המחשבת את הנפח של הקוביה (רוחב * גובה * אורך)
ג. פונקציה המדפיסה את כל הנתונים של הקוביה.
על פונקציה זו להדפיס את הרוחב הגובה והאורך, אך פרמטר אופציונאלי שיתקבל לפונקציה, עשוי לבקש ממנה להדפיס גם את הנפח שלה. (בעזרת הפונקציה הקודמת..)
הערה: כמו שניגשנו לשדות (משתנים) של מחלקה בתוך פונקציה באמצעות המשתנה 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
נשים לב לכמה דברים:
salt = Product(name="Salt", price=5.5)
salt = Product(x="Salt", y=5.5)
אני מקווה שברור לכם למה, אם לא, חזרו לפרק של הפונקציות 🙂
צור מחלקה המייצגת שולחן.
עליך ליצור לשולחן את השדות:
השולחנות שהמחלקה מייצגת, כולם מתוצרת איקאה, כך שלמחלקה גם יהיה שדה בשם company שיאותחל תמיד בשם IKEA.
כמו כן, צור למחלקה פונקצית איתחול (בנאי), שתאתחל את השדות אורך ורוחב ומחיר.
לשדה מחיר עליך לציין ערך ברירת מחדל של 100.
בנוסף, למחלקה תהיינה הפונקציות הבאות:
לסיום, צור קוד היוצר שני שולחנות, אחד עם מחיר “כדאי”, ואחד עם מחיר “לא כדאי”. (כאשר ה 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 בשביל לציין את שם המשתנה המכיל את המופע, הוא מנהג… יכולנו להשתמש בשם אחר לכך.
אם אנו מתעניינים ואוהבים את ה “מנהגים” הללו, אנו נרצה להציץ במסמך הזה.
אעיר ואוסיף, שהמנהגים הללו הם אחד ההבדלים בין מתכנת מקצועי לשאינו כזה…
חבר’ה, יש לי עוד הרבה דברים לספר לכם על מחלקות.
יש מלא דברים שאפשר לעשות עם מחלקות שלא דיברנו עליהם, אני אנסה להכיר לכם את העיקריים שבהם שלא ציינו.
מחלקות בפייתון (ובכל שפה) הוא נושא בסיסי וחשוב. בשיעור הבא נמשיך ללמוד על נושאים מתקדמים יותר בעולם הזה.