פרק קודם:
פייתון 16 - מחלקות חלק א1 | class MyClass: |
כדי להגדיר את המחלקה כל מה שצריך זה לכתוב את המילה class
:
1 | class MyClass: |
*זוכרים pass
? זה סתם שומר מקום ששמים אם אין שום דבר לשים אחרי נקודותיים.
ניתן להגדיר מחלקה ריקה שבעצם אין לה מידע ואין לה התנהגות.
מחלקות הן רק התרשים של המחלקה לאובייקט/עצם.
לעצם קוראים “Instance”.
כדי ליצור עצם קוראים למחלקה כמו פונקציה, וזה בעצם קורא לבנאי.
בנאי הוא פונקציה שמופעלת כאשר העצם נוצר.
1 | class MyClass: |
חתימה של פונקציה מגדירה את השם שלה, פרמטרים ומה היא מחזירה
1 | def __init(self, variable): |
הבנאי היא פונקצייה בשם _init_
והיא תמיד נקראית כך!
הפרמטר הראשון self
הוא מילה שמורה בפייתון ומגדיר את “עצמו” בתור משתנה.
הפרמטר השני variable
הוא משתנה רגיל שמעבירים לפונקצית הבנאי.
1 | self.Variable = variable |
המשתנה self
מגדיר את העצם שאותו יוצרים.
כל גישה למשתנה self
ניגשת לעצם החדש שיווצר.
לכן השורה הזו יוצרת משתנה Variable בכל עצם שנוצר.
ניתן לגשת למשתנה משום שמשתנה כזה הוא ציבורי.
1 | print(instance.Variable) |
עוד מעט נראה איך מגדירים משתנה פרטי שאי אפשר לגשת אליו מבחוץ!
ניתן להגדיר משתנה גם במחלקה במקום בגוף הבנאי:
1 | class MyClass: |
ההבל בין משתנה מחלקה למשתנה עצם הוא שמשתנה מחלקה נוצר גם בלי יצירה של עצם מהמחלקה.
1 | class MyClass: |
אפילו יותר מזה, אם משנים את המשתנה הזה ויוצרים עצם חדש, אז העצם ייקבל את הערך הזה גם כן.
1 | class MyClass: |
משתנה כזה לעיתים נקרא סטטי - Static.
משתנים ופונקציות סטטיות מוגדרות ברמת המחלקה ולא ברמת העצם.
זה למה ניתן לבצע הדפסה כזו:
1 | print(MyClass.ClassVariable) |
פונקציות מוגדרות מתחת למחלקה כמו פונקציות רגילות.
הגישה אליהן בעזרת הנקודה .
1 | class MyClass: |
1.
מה ההבדל בין מחלקה לעצם?
מחלקה היא כמו תבנית ועצם הוא הבנייה שלה.
מהו בנאי?
פונקציה שנקראת כדי לאתחל את העצם של המחלקה.
מה ההבדל בין משתנה סטטי למשתנה עצם?
משתנה עצם הוא שונה לכל עצם כאשר משתנה סטטי הוא למחלקה.
לכל עצם של המחלקה יש משתנה סטטי שונה אך המשתנה הסטטי של המחלקה נשאר זהה.
משתנה מחלקה:
1 | class MyClass: |
משתנה עצם:
1 | class MyClass: |
1 | class Calculator: |
1 | class Animal: |
ארבעת העקרונות המנחים אותנו הם:
עקרון הכימוס מגדיר שיש לשמור על מידע והמצב של המחלקה בתוך המחלקה ושלא תהיה גישה מבחוץ,
למשל הדוגמא הזו לא שומרת על עקרון הכימוס:
1 | class Bird: |
כדי לשמור על עקרון הכימוס ניתן להוסיף שני קווים תחתוניים:
1 | class Bird: |
ניתן לראות שפייתון מייצר שם שונה למשתנה כאשר מוסיפים קווים תחתוניים:
שם המשתנה מאחורי הקלעים הוא בעצם _Bird__Saying.
אבל עצם השימוש בקווים תחתוניים אומר למתכנת שזה משתנה פרטי!
כפי שהגדרנו בחלק הראשון - ירושה זה לקבל מידע ופונקציות ממחלקת הבסיס.
על מנת לרשת מחלקה משתמשים בסוגריים מעוגלים אחרי שם המחלקה עם מחלקת הבסיס:
1 | class Person: |
הירושה מתבצעת בשורה הזו:
1 | class Worker(Person): |
הגדרנו את SayHello שכותב שלום עם השם במחלקת הבסיס.
1 | def SayHello(self): |
במחלקה הנגזרת ירשנו את הפונקציה הזו ומימשנו פונקציה זהה בחתימה.
1 | def SayHello(self): |
כדי לבצע את זה צריך להגדיר פונקציה עם אותה השם ועם אותם הפרמטרים אחרת זו תהיה פונקציה חדשה.
אך הפונקציה הזו רק מסתירה את הפונקציה מאחורי הקלעים, עדיין ניתן לקרוא לפונקציה המקורייה וזה מה שעשינו בעצם בהגדרת ה-else:
1 | else: |
בעזרת השם של מחלקת הבסיס אנחנו יכולים לקרוא לפונקציה מתוך הפונקציה החדשה שהגדרנו במחלקה הנגזרת.
אם שמתם לב גם לבנאי קראנו בצורה הזו!
1 | def __init__(self): |
דרך שנייה שניתן לגשת למחלקת הבסיס במחלקה הנגזרת היא בעזרת super
.
1 | def __init__(self): |
יש שני דרכים לבצע הפשטה:
1 | class Animal: |
1 | class Animal: |
אחד ההבדלים המובהקים הם שאנחנו יוצרים מחלקה נוספת שמסתירה עקרונות מסוימים כמו סוג החיה.
הפשטה כזו היא לעיתים מה שאנחנו רוצים כדי ליצור קוד מובן יותר למתכנתים.
זאת על מנת להסביר את הקוד יותר טוב מבלי לכתוב הערות מיותרות, אחרת היינו צריכים להסביר את מהות המשתנה Type
.
1 | class Animal: |
במושגי תכנות מונחה עצמים זה לא מה שאנחנו רוצים כי יש לנו את הכלים לבצע את זה בצורה יותר מסודרת ויותר מובנת.
פייתון היא שפה דינאמית - זאת אומרת שאנחנו יכולים לעשות כל מיני טריקים כמו להעביר משתנים שלא ציפינו להם:
1 | def MyFunc(number): |
התוצאה:
לפייתון יש אפשרות לומר מה הם המשתנים שאנחנו מעבירים ומחזירים מפונקציות:
1 | def MyFunc(number: int) -> int: |
השימוש של הרמזים האלו לא מכריח את פייתון להשתמש במשתנה מסוג מספר - כי פייתון היא שפה דינאמית.
היא יכולה לקבל כל משתנה וליצור משתנים בזמן ריצה!
תריצו את הקוד הבא ותראו מה קורה!
1 | class Person: |
מה שזה מאפשר לעשות זה פולימורפיזם עם אובייקטים נגזרים מאותה מחלקת בסיס:
1 | class Animal: |
א.
1 | class DBAccess: |
ב.
1 | class Spell: |
ג.
1 | class Furniture: |
2.
בעזרת תכנות מונחה עצמים נממש משחק טריויה!
למשחק יהיה תפריט ראשי שניתן לבחור כמות שחקנים.
אחרי שבוחרים כמות שחקנים כל שחקן ייקבל שם רנדומלי (צורת המימוש יכולה להיות שלכם).
מאיפה התכנית מקבלת שאלות:
תשתמשו באבסטרקציה ופולימורפיזם כדי לכתוב קוד שלוקח את השאלות שלו ממקור כלשהו.
כמו כן עליכם לקבוע כיצד שאלה נראית, כמה תשובות יש לה.
יש לשייך שאלה לקטגוריה מסוימת.
לכל שאלה יש רמת קושי.
מהלך המשחק:
כדי לייצר מספר רנדומלי ניתן להשתמש בקוד הבא:
1 | import random |
random.randint(min,max)
מייצר מספר בין min ל-max.
א.
הקוד מממש את עקרון הכימוס.
מאחורי הקלעים הוא שומר משתנה פרטי בשם __realAccess
ולא מאפשר לקוד מבחוץ לגשת אליו בקלות.
ב.
העקרון הממומש הוא ירושה ופולימורפיזם,
הסיבה לכך שההתנהגות שלנו משתנה בין קסם לקסם שממומש בעזרת מחלקות,
השימוש בפולימורפיזם מתבטא בכך שאנחנו בוחרים בקסם כלשהו ולא ידוע לנו איזה קסם נבחר,
ואנחנו משתמשים במתודת Cast
שכל מחלקת קסם מממשת.
ג.
העקרון המומש הוא עקרון ההפשטה.
ההפשטה היא עצם קיום מחלקת Furniture
אשר מעניקה יכולת אבסטרקטית לתיאור רהיטים.
מכיוון ש”רהיט” זה רעיון מופשט - יכול להיות כיסא, יכול להיות שולחן וכדו’…
קודם נעצב את המחלקות וניתן מבט ראשון התוכנה שלנו תיראה.
המחלקות הראשונות שלנו יהיו מחלקות מידע (מודלים!)
מחלקת שאלה היא מחלקה שמחזיקה:
טוען את רשימת השאלות ומחזיר רשימה של קטגוריות
מחזיר שם של שחקן ייחודי
המחלקה תחזיק את רשימת השקחנים ותיתן לנו את השחקן הבא שיישחק.
מחלקת המשחק הראשית מבצעת את מהלך המשחק
יהיו שני אופציות בתפריט הזה - שחק ולצאת
לאחר מכן הוא יריץ את המשחק
העיצוב הבסיסי נתן לנו כיוון והמימוש הוא כבר חלק מהקוד.
את הקוד לפתרון ניתן למצוא כאן:
Github
יותר מדי מחלקות קוד ולכן לא יכולתי לעלות את הכל במקטע אחד.
כדי להריץ יש להריץ את Main.py.
1 | python Main.py |
הפתרון מכיל מחלקות בסיסיות לטיפול בקוד, שימו לב שהרוב נופל על מחלקת המשחק,
היינו יכולים לפצל את המחלקה הזו עוד יותר אך פונקציות היו מספיק מועילות.
מילת המפתח כאן היא - מספיק.
כמובן שניתן לפצל יותר ויותר, לכתוב מחלקות נוספות ולסבך את הקוד עוד יותר.
אך אנחנו צריכים למצוא את האיזון הדק בין מורכבות לפשטות.
בפרק הבא נעסוק בעיצוב מחלקות, כיצד לגשת לחשיבה נכונה ונלמד עקרונות עיצוב בסיסיים:
פייתון 18 - מחלקות חלק ג