פייתון/פייתון גרסה 2/מחלקות/זמני2
מחלקה היא טיפוס חדש המוגדר על ידי המתכנת. בשונה מרשימה, הגישה לאיברי מחלקה איננה מספרית, אלא שמית. בפרק זה נלמד כיצד מגדירים ומשתמשים במחלקה, וכן כיצד כותבים תוכניות ארוכות יותר ממספר שורות.
הצורך במחלקות
עריכהנחשוב על תוכנית לניהול מלאי של חנות. בחנות יש סוגי פריטים שונים, שלכל אחד מהם מספר קטלוגי משלו, שם, מחיר, וכמות (כלומר, כמה יש בחנות כעת). אנו רוצים ליצור בסיס נתונים לטיפול במידע הזה, למשל לעדכון מחיר של פריט, לעדכון כמות יחידות פריט במקרה של מכירה, וכדומה. בפרט, היינו רוצים לשמור רשימה שכל אחד מאיבריה יתאר פריט.
פייתון אינה כוללת טיפוס מתאים לתיאור פריט במלאי החנות - כל אחד מטיפוסי השפה יכול לתאר דברים פשוטים מאד בלבד (כמו מספרים). בפרק זה נראה כיצד להגדיר טיפוסים חדשים ולהשתמש בהם.
מהי מחלקה?
עריכהמחלקה היא טיפוס חדש, שאנו מגדירים את שמו ומרכיביו כרצוננו. למחלקה יש שדות, שהם איברים בעלי טיפוסים ושמות שאנו בוחרים.
לדוגמה, בדוגמה שראינו בצורך במחלקות, נרצה ליצור טיפוס חדש שייקרא item
. מחלקה זו תכיל את השדות הבאים:
- מספר קטלוגי, מסוג שלם
- שם, מסוג מחרוזת
- מחיר, מסוג נקודה צפה
- כמות (כמה יש במלאי), מסוג שלם לא-שלילי
עבודה עם מחלקות - גרסה ראשונית
עריכהלמחלקות פייתון יש תחביר שנראה מוזר מעט במבט ראשון. נתחיל, לכן, בגרסה ראשונית שמעלתה היא דמיונה למה שראינו בספר עד כה. מאוחר יותר נעבור לגרסה יותר מציאותית.
הגדרת מחלקה
עריכהמגדירים מחלקה בצורה הבאה:
class <name>
<class_body>
כאשר name הוא שם המחלקה, וclass_definition היא גוף המחלקה.
נתחיל במחלקה בעלת גוף ריק, שאותה נגדיר כך:
class item
pass
הגדרת המחלקה הנ"ל מודיעה שיש כעת טיפוס חדש, ששמו item
. גוף המחלקה ריק (זו משמעות המילה השמורה pass
).
עצמים מהטיפוס החדש
עריכהלאחר שהגדרנו את הטיפוס החדש, נוכל להשתמש בו כמו בכל טיפוס אחר. הקוד הבא, לדוגמה, מייצר עצם חדש מסוג המחלקה הנ"ל, ומקשר את השם shoko
אליה.
shoko = item()
שדות המחלקה
עריכהכעת כשיש לנו עצם מהמחלקה החדשה, אפשר להוסיף לו שדות. שדה הוא איבר מהמחלקה, המתאים לתכונה יחידה של המחלקה.
לדוגמה, נניח שshoko הוא משתנה מסוג item. כדי לקבוע את מחירו ל12.90, נכתוב:
shoko.price = 12.90
כדי לקבוע את שמו כ"shoko", נכתוב:
shoko.name = ,shoko,
כדי להדפיס את שמו ואת מחירו, נכתוב:
print "The price of %s is %f" % (shoko.name, shoko.price)
למעשה אפשר לרשום פונקציה חדשה שתייצר עצם בעל שדות מתאימים. נרשום פונקציה חדשה המייצרת עצם מסוג item:
def make_item(catalog_number, price, name, num):
itm = item()
itm.catalog_number = catalog_number
itm.price = price
itm.name = name
itm.num = num
return itm
הפונקציה מקבלת פרמטרים התחלתיים לסוג הפריט, ומחזירה עצם מסוג item ששדותיו מאותחלים למשתנים אלה.
כדי לייצר עצם חדש, נוכל לבצע את הקריאה הבאה, לדוגמה:
shoko = make_item(23, 19.90, 'shoko', 100)
דוגמאות ביניים
עריכהנראה כעת מספר דוגמאות של פונקציות הפועלות על עצמי מחלקה.
עדכון מחיר פריט
עריכההפונקציה הראשונה מקבלת סוג פריט ותוספת מחיר, ומעלה את מחיר סוג הפריט בתוספת:
def raise_price(itm, amount):
itm.price += amount
עדכון מספר איברי פריט
עריכההפונקציה הבאה שנסה לכתוב מקבלת סוג פריט, ומספר היחידות שרוצים לקנות מתוכו. היא בודקת כמה יחידות אפשר לקנות, מעדכנת את מספר היחידות במלאי, ומחזירה את המחיר הכולל שיש לשלם על הקניה:
def update_num(itm, how_many):
itm.num -= how_many
return itm.price * how_many
נשים לב שבפונקציה יש טעות. במקרה בו מספר היחידות שרוצים לקנות גדול ממספר היחידות במלאי, יימכרו יותר מדי יחידות. נשנה מעט את הפונקציה.
def update_num(itm, how_many):
how_many = itm.num if itm.num < how_many else how_many
itm.num -= how_many
return itm.price * how_many
(השורה הראשונה בגוף הפונקצייה
how_many = itm.num if itm.num < how_many else how_many
מחשבת כמה איברים אפשר באמת לקנות: זהו מספר האיברים במלאי אם מספר האיברים במלאי קטן מהמספר המבוקש, ואחרת פשוט מספר האיברים המבוקש.)
הדפסת נתוני פריט
עריכההפונקציה האחרונה שנכתוב כאן מקבלת עצם פריט, ומדפיסה למסך את נתוניו.
def display_item(itm):
print "name: %s, catalog number: %d, price: %f, in stock: %d\n" % \
(itm.name, itm.catalog_number, itm.price, itm.num)
דוגמה מסכמת
עריכהנמשיך בדוגמה שתסכם את רוב מה שלמדנו על מחלקות.
התוכנית
עריכההתוכנית הבאה היא תוכנת ניהול פשוטה מאד לחנות מכולת:
class item:
pass
def make_item(catalog_number, price, name, num):
itm = item()
itm.catalog_number = catalog_number
itm.price = price
itm.name = name
itm.num = num
return itm
def update_num(itm, how_many):
how_many = itm.num if itm.num < how_many else how_many
itm.num -= how_many
return itm.price * how_many
def display_item(itm):
print "name: %s, catalog number: %d, price: %f, in stock: %d\n" % \
(itm.name, itm.catalog_number, itm.price, itm.num)
items = []
items.append( make_item(23, 12.90, 'shoko', 100) )
items.append( make_item(109, 5, 'roll', 100) )
items.append( make_item(22, 2.3, 'kartiv', 5) )
items.append( make_item(33, 1.0, 'mastik', 10) )
items.append( make_item(1000, 5, 'pita', 1000) )
items.append( make_item(2233, 23, 'humus', 20) )
done = False
while done == False:
print 'The items in the store are:'
for i in range(6):
display_item(items[i])
i = input('Which item would you like to purchase? ')
if(i > 6):
done = True
else:
update_num(items[i], 1)
פירוט
עריכההמחלקה והפונקציות הפועלות עליה
עריכהראשית, הנה הגדרת מחלקה הפריט ופונקציות העזר שכבר ראינו:
class item:
pass
def make_item(catalog_number, price, name, num):
itm = item()
itm.catalog_number = catalog_number
itm.price = price
itm.name = name
itm.num = num
return itm
def update_num(itm, how_many):
how_many = itm.num if itm.num < how_many else how_many
itm.num -= how_many
return itm.price * how_many
def display_item(itm):
print "name: %s, catalog number: %d, price: %f, in stock: %d\n" % \
(itm.name, itm.catalog_number, itm.price, itm.num)
הקוד לניהול הפריטים
עריכהכעת לקוד המריץ את ניהול הפריטים:
items = []
items.append( make_item(23, 12.90, 'shoko', 100) )
items.append( make_item(109, 5, 'roll', 100) )
items.append( make_item(22, 2.3, 'kartiv', 5) )
items.append( make_item(33, 1.0, 'mastik', 10) )
items.append( make_item(1000, 5, 'pita', 1000) )
items.append( make_item(2233, 23, 'humus', 20) )
done = False
while done == False:
print 'The items in the store are:'
for i in range(6):
display_item(items[i])
i = input('Which item would you like to purchase? ')
if(i > 6):
done = True
else:
update_num(items[i], 1)
ראשית מגדירים את תכולת המלאי, המכיל 6 סוגי פריטים:
items = []
items.append( make_item(23, 12.90, 'shoko', 100) )
items.append( make_item(109, 5, 'roll', 100) )
items.append( make_item(22, 2.3, 'kartiv', 5) )
items.append( make_item(33, 1.0, 'mastik', 10) )
items.append( make_item(1000, 5, 'pita', 1000) )
items.append( make_item(2233, 23, 'humus', 20) )
הלולאה:
done = False
while done == False:
print 'The items in the store are:'
for i in range(6):
display_item(items[i])
i = input('Which item would you like to purchase? ')
if(i > 6):
done = True
else:
update_num(items[i], 1)
פועלת כל עוד לא הקליד המשתמש מספר גדול מ6.
בתוך הלולאה, ראשית מדפיסים את הפריטים:
print 'The items in the store are:'
for i in range(6):
display_item(items[i])
לאחר מכן מבקשים מהמשתמש את הפריט שברצונה לרכוש:
i = input('Which item would you like to purchase? ')
כל שנותר הוא (לבדוק אם הפריט חוקי ו) לטפל בבקשה:
if(i > 6):
done = True
else:
update_num(items[i], 1)
עבודה עם מחלקות - גרסה עדיפה
עריכהבפועל, אין משתמשים במחלקות בצורה שראינו עד כה. כעת נראה גרסה עדיפה.
הכנסת הפונקציות למחלקה
עריכהclass item:
def __init__(self, catalog_number, price, name, num):
self.catalog_number = catalog_number
self.price = price
self.name = name
self.num = num
def update_num(self, how_many):
how_many = self.num if self.num < how_many else how_many
self.num -= how_many
return self.price * how_many
def display(self):
print 'name: %s, catalog number: %d, price: %f, in stock: %d\n' % \
(self.name, self.catalog_number, self.price, self.num)
נוכל לשים לב לשינויים החדשים:
- הפונקציות הוכנסו לתוך המחלקה.
- הפרמטר הראשון בכל פונקציה נקרא
self
. - הפונקציה
create_item
שינתה את שמה, ונקראת עתה בשם (המוזר מעט)__init__
.
יצירת עצמי מחלקה
עריכהנוכל ליצור עצמי מחלקה בצורה הבאה:
item(23, 19.90, 'shoko', 100)
פקודה זו קוראת לפונקציה __init__
שראינו מקודם (זו שהחליפה את make_item
). מהפרמטר הראשון, self
, נתעלם לרגע. שאר הפרמטרים (לדוגמה catalog_number
), מקושרים לעצמים שאנו מעבירים (לדוגמה 23).
קריאה זו מייצרת עצם, ואפשר כמובן לקשר שם לעצם זה:
shoko = item(23, 19.90, 'shoko', 100)
אפשר גם להכניס עצמים כאלה לרשימה.
items = []
items.append( item(23, 12.90, 'shoko', 100) )
items.append( item(109, 5, 'roll', 100) )
items.append( item(22, 2.3, 'kartiv', 5) )
items.append( item(33, 1.0, 'mastik', 10) )
items.append( item(1000, 5, 'pita', 1000) )
items.append( item(2233, 23, 'humus', 20) )
שימוש בעצמי המחלקה
עריכהלאחר שייצרנו עצם מחלקה, אפשר לקרוא לפונקציות שהכנסנו לתוכו. לדוגמה,
shoko = item(23, 19.90, 'shoko', 100)
print shoko.display()
תקרא לפונקציה display
, וprint
תדפיס את התוצאה.
שכתוב הדוגמה המסכמת
עריכהנשכתב את הדוגמה המסכמת שראינו מקודם, כך שתעבוד עם מחלקה פייתונית בגרסה העדיפה.
class item:
def __init__(self, catalog_number, price, name, num):
self.catalog_number = catalog_number
self.price = price
self.name = name
self.num = num
def update_num(self, how_many):
how_many = self.num if self.num < how_many else how_many
self.num -= how_many
return self.price * how_many
def display(self):
print 'name: %s, catalog number: %d, price: %f, in stock: %d\n' % \
(self.name, self.catalog_number, self.price, self.num)
items = []
items.append( item(23, 12.90, 'shoko', 100) )
items.append( item(109, 5, 'roll', 100) )
items.append( item(22, 2.3, 'kartiv', 5) )
items.append( item(33, 1.0, 'mastik', 10) )
items.append( item(1000, 5, 'pita', 1000) )
items.append( item(2233, 23, 'humus', 20) )
done = False
while done == False:
print 'The items in the store are:'
for i in range(6):
items[i].print()
i = input('Which item would you like to purchase? ')
if(i > 6):
done = True
else:
items[i].update_num(1)
הסבר מעט יותר מעמיק
עריכהפונקציית הבנייה __init__
עריכההפרמטר self
עריכהאיברי מחלקה "פרטיים"
עריכהclass item:
def __init__(self, catalog_number, price, name, num):
self._catalog_number = catalog_number
self._price = price
self._name = name
self._num = num
def update_num(self, how_many):
how_many = self._num if self._num < how_many else how_many
self._num -= how_many
return self._price * how_many
def display(self):
print 'name: %s, catalog number: %d, price: %f, in stock: %d\n' % \
(self._name, self._catalog_number, self._price, self._num)
כדאי לדעת: C++ |
פונקציות מחלקה מיוחדות
עריכהיתרונות שיטת המחלקות השניה
עריכההגרסה שראינו עד כה בעייתית ממספר סיבות. את מרביתן אפשר להבין לעומק רק לאחר הבנה עמוקה יחסית של מחלקות. בקצרה נציין כדוגמה שתי בעיות עם ההפוקנציה המעדכנת את מספר איברי הפריט, update_num
.
כימוס
עריכהכפי שציינו מקודם, קל לפעמים לטעות כאשר ניגשים ישירות לשדות מחלקה. נסיוננו הראשון, לדוגמה,
def update_num(itm, how_many):
itm.num -= how_many
return itm.price * how_many
פועל בצורה שגויה כאשר מספר האיברים המבוקש גדול מהמלאי.
בתוכנית גדולה מאוד, גם אם בדיעבד היינו מזהים בעיה זו, היה קשה מאד לשנות
שיוך
עריכהנניח שהיינו מרחיבים את תוכניתנו גם לניהול העובדים בחנות. ייתכן מאד שהיינו מגדירים עוד פונקציה update_num
, אלא שמשמעותה היתה שונה - שינוי מספר העובדים בחנות. למרבה הצער, פונקציה זו היתה "מתנגשת" עם הפונקציה שהגדרנו זה עתה. טוב היה לו היינו יכולים להודיע שהפונקציה update_num
הראשונה שהגדרנו "שייכת" למחלקה item
.