פרק קודם:
פייתון 6 - לולאותמה נלמד:
- שימוש חוזר של קוד
- מה זה פונקציות, מתודות ומה הן מגדירות
- איך להגדיר פונקציות בפייתון
- עקרון האחריות היחידה - Single Responsibility principal
שימוש חוזר של קוד
הקוד הראשון שאי פעם רץ היה מלפני שנים רבות - למען האמת עוד בשנות ה-40!
המתכנתים הראשונים היו בתקופת מלחמת העולם השנייה - אני קורא להם מתכנתים כי הם היו הראשונים שהשתמשו במחשב אלקטרוני!
אחרי 80 שנה של פיתוח וכתיבת קוד, כנראה שאנחנו לא נהיה הראשונים לכתוב אלגוריתם כלשהו - במיוחד קוד נפוץ כמו ממוצע של מספרים.
לכן עקרון שחוזר על עצמו הוא - שימוש חוזר של קוד
.
במילים פשוטות יותר מתכנתים נמנעים מלהמציא את הגלגל מחדש.
בתכנות קיימים הרבה טכניקות לשימוש חוזר של קוד ושיתוף תבניות - היום נלמד על הדרך הכי פשוטה - פונקציות
.
מה הן פונקציות
כבר הראנו בפרק הקודם כיצד לממש חישוב ממוצע:
1 | arr = [1,2,3] |
זהו עקרון בסיסי בתכנות, לכל דבר יש קלט ופלט בצורה כלשהי - כאן אנחנו מכניסים מערך arr
ומוציאים את הממוצע שלו.
לכן arr
הוא הקלט שלנו וavg
הוא הפלט שלנו.
תדמיינו שאנחנו מסתירים את הלולאה ורואים בסופו של דבר רק את הקלט והפלט.
קופסא שחורה -
מושג המתאר רכיב או קטע קוד אשר דרך המימוש שלו מוסתר מאיתנו.
זאת אומרת איננו יודעים כיצד הוא עובד, מבחינתנו אנחנו יודעים שיש קלט ופלט.
פונקציה זו קופסה שאנחנו נותנים לה שם, והיא יכולה להוציא פלט או להחליט לא להוציא פלט, והיא יכולה לקבל קלט או לא לקבל קלט זו החלטה של כותב הפונקציה.
בדוגמא הקודמת כעת נתנו לקופסה שם ורשמנו מה היא מוציאה ומה היא מקבלת:
לקופסה קוראים average
, היא מקבלת מערך
ומחזירה מספר
.
טרמינולוגיה חשובה לדעת
לתת שם לרכיב כלשהו בתכנות זה קשה - השם צריך לתאר בקצרה ובמדויק מה הרכיב הזה מבצע.
לפונקציות מקנים שמות שמתארים פעולה כלשהי כי פונקציה מבצעת פעולה כלשהי.
בדרך כלל נבחר בפעלים.
למשל:
Average()
Sum()
BuyItem()
Sell()
CalculateBalance()
Wave()
שמות פחות טובים:
Do
- מאוד כללי ולא ברור מה הפונקציה עושהApples
- הוא זה עושה משהו עם תפוחים אבל זה ממש לא ברור, אל תבחרו בשמות עצם!DoAndUpdateAndTransform
- כאן יש לנו שמות של פעלים אבל הבעיתיות היא שזה עושה מעבר למשהו אחד ומבלבל מאוד את הפונקציה!
ניתן לקרוא לפונקציות בצורה שונה:
- מתודות -
Methods
- בד”כ מתייחס לפונקציות שלא מחזירות ערך. - פעולות -
Actions
- קריאה -
Invocation
- מאוד נדיר כמעט לא משתמשים בזה.
איך לדבר על פוקנציות:
כפי שאמרנו אם אנחנו רוצים להציג פונקציה אנחנו צריכים להסתכל על החתימה שלה,
החתימה שלה זו הדרך שבה אנחנו מגדירים אותה - פלט, השם שלה והקלט שלה.
בד”כ אנחנו אומרים שאנחנו “קוראים” לפונקציה או “מפעילים” פונקציה או “עושים פעולה”.
למשל:
1 | arr = [1,2,3] |
ככה אנחנו קוראים את הקוד:
אתחלנו מערך בשם arr
לערכים 1
,2
ו3
.
לאחר מכן קראנו לפונקציה average
עם המערך והשמנו את הערך שלו במשתנה avg
.
לפלט וקלט יש גם שמות נוספים,
לקלט אנחנו קוראים פרטמרים - Parameters
.
ופלט לעיתים אנחנו אומרים “ערך החזרה” - Return value
.
מכיוון שיכול להיות יותר מקלט אחד אבל בד”כ רק פלט אחד.
תרגיל
- חשבו על דברים מהחיים שלכם שאתם יכולים להגדיר כקופסה שחורה ותתנו לזה שם של פעולה.
- תתארו את הקוד הנ”ל:
1 | arr = [2,5,6] |
- כמה דברים מהחיים שלי:
- MakeCoffee(Coffee, HotWater) - פונקציה שבה הקלט הוא קפה טחון, מים חמים והפלט הוא קפה שחור חם.
- Drive(Location, Fuel) - נוסע למיקום מסוים, צורך דלק.
- Write(Idea, Keyboard, PC) - מקבל רעיון, מקלדת ומחשב וכותב את הרעיון.
2.
בקטע הקוד הזה אתחלנו מערך לערכים 2,5,6.
לאחר מכן קראנו לפונקציה sum
עם המערך והערך שהתקבל מהפונקציה הזו - הסכום של המערך עובר לפונקציה print
שלה אנו קוראים מיד.
1 | s = sum(arr) |
פונקציות בפייתון
ראינו כבר דוגמאות לקריאות לפונקציות,
שני הפונקציות הכי נפוצות שהשתמשנו בהן הן print
וinput
.
לכל אורך המאמר קראתי להן פעולות על מנת לא לבלבל בטרמינולוגיה מתקדמת אך מעתה נקרא להן פונקציות.
התחביר בפייתון
הגדרה של פונקציה נראית כך:
1 | def NameOfFunction(input1, input2): |
def
היא מילה שמורה בפייתון שאומרת שזו הגדרה של פונקציה.
לאחר מכן כותבו את שמה של הפונקציה.
הסוגריים חייבים להופיע תמיד ובהם 0 פרמטרים או יותר שהפונקציה מקבלת כקלט.
הנקודותיים מתחילו בלוק חדש כמו בתנאים ולולאות.
אם הפונקציה אינה עושה דבר ניתן להשתמש ב-Pass
.
פורמט שמות שאני משתמש בו רבות הוא UpperCamelCase
- כל מילה חדשה מתחילה באות גדולה.
למשל:
1 | def CalculateSum(): |
אם אנחנו רוצים יותר מפרמטר אחד אנחנו מפרידים אותם ע”י פסיקים.
כמה דוגמאות:
1 | def Average(array): |
על מנת לקרוא לפונקציה היא צריכה להיות מוגדרת לפני הקוד שקורא לה.
תנסו להריץ את הקוד הנ”ל:
1 | result = Sum(1,2) |
בדוגמאות הקודמות הראנו פונקציות שמחזירות ערך, ניתן גם פשוט לא להחזיר ערך, למשל:
1 | def printFruit(name, price): |
עקרון האחריות היחידה - Single Responsibility princple
לקטע קוד יש אחריות מסוימת, זה מה שהקוד מטפל בו.
למשל אם זו פונקציה שמחשבת ממוצע, האחריות של הפונקציה הזו היא להחזיר ממוצע בלבד.
קוד טוב וקריא הוא קוד שמבצע דבר אחד בלבד, למה אבל זה ככה?
כאשר קוד מבצע יותר ממשהו אחד, קשה יותר לטפל בבאגים שיכולים להיווצר מזה, ולנו כבני אדם קשה יותר להתמקד בכמה דברים בו זמנית שקוראים.
אם השכל שלנו יתמקד בדבר אחד בלבד כל פעם, יהיה לנו הרבה יותר קל לכתוב ולשדרג קוד.
דוגמא לקוד לא טוב שעושה כמה דברים:
1 | def CalculateResults(arr, a, b, c): |
הקוד הזה מעלה תהיות ושאלות -
- מה זה Results?
- האם יש רעיון מאחורי החזרה של מערך?
- למה
a,b,c
איננו מערך גם כן?
דוגמא לקוד שמבצע את אותם דברים אך עם העקרון לאחריות יחידה - SRP
בקיצור.
1 | def CalculateAverage(arr): |
הדבר הראשון שעשינו זה פיצלנו את תחום האחריות לשתי פונקציות,
אל תתביישו לכתוב פונקציות נוספות.
זה גם פיצל את ערך ההחזרה, מקודם החזרנו מערך עם שני ערכים - קשה מאוד לדעת ככה מה מהם הערכים,
אך לאחר הפיצול נתנו שמות טובים למשתנים שלנו והכל הרבה יותר מובן!
לקוד קריא ופשוט יש ערך יותר גבוה מקוד מתוחכם ומסורבל.
תרגילים
- כתבו פונקציה שמקבלת מחרוזת ומחריזה את המחרוזת ההופכה שלה, למשל:
“abc” -> “cba”
זוכרים אינדקס שלילי?
מימשו את פונקציה כך שכל הבדיקות ייצאו חיוביות:
1 | def Reverse(str): |
- כתבו פונקציה שבודקת אם המספר הוא פולינדרומי
דוגמאות למספר פולינדרומי:
- 1234321
- 2004664002
1 | def IsPalindrome(number): |
- לתוכנת מסד נתונים נדרשנו לכתוב פונקציה שבודקת אם הסיסמא שהכנסו היא חזקה,
סיסמא חלשה מכילה מספרים בלבד
סיסמא בינונית מכילה מספרים ואותיות,
סיסמא חזקה מכילה אותיות, מספרים וסימנים מיוחדים.
למשל:
- חלש - 123456
- בינוני - a123bcd
- חזק - 9t7#aa
כרגע אתם יכולים לפתח פתרון נאיבי - יש מגוון צורות לפתור את זה.
- כתבו תוכנה שמבצעת פקודות ע”פ הקלט של המשתמש.
התוכנה תעבוד בלולאה, ועל כל קלט הוא ייבדוק אם קיימת פקודה שהמשתמש הקיש.
פקודות למימוש:
- “hello” -> התוכנה תדפיס “Welcome”.
- “goodbye” -> התוכנה תדפיס “See you again” ותצא
- “sum” -> הפקודה תבקש שני מספרים מהמשתמש ותדפיס את התוצאה שלהן.
- “register” -> הפקודה תבקש שם משתמש ותזכור את המשתמש כך שאם מנסים להירשם עם אותו שם משתמש זה מדפיס לו שכבר קיים.
- “find” -> הפקודה תבקש שם משתמש ותבדוק אם קיים במערכת.
1 | def Reverse(str): |
הסבר:
השתמשנו באינדקס ההפוך, שאותו רושמים כמספר שלילי, אותו חישבנו בעזרת האינדקס שאותו אנחנו לוקחים מenumerate.
הפונקציה enumerate מחזירה לנו שני דברים,
הראשון זה האינדקס והשני זה התו שבמחרוזת או במערך.
שימו לב לשימוש בfor, זה יכול להיות מאוד שימושי!
1 | def IsPalindrome(number): |
הסבר:
כאן אנחנו יכולים בתכלס לעשות את אותו reverse שעשינו בתרגיל הראשון.
לקחת את המספר כמחרוזת ולא כמספר רגיל.
אך כאן רציתי להראות לכם שניתן לחשב את המספר הפולינדרומי כמספר ולא מחרוזת.
מה שאנחנו מבצעים זה לוקחים את המספר הכי ימני בעזרת פעולת המודולו % עם 10.
למשל אם אנחנו עושים מודולו בין 13 ו 10 אנחנו נקבל 3:
10 % 13 = 3
לאחר מכן אנחנו לוקחים את המספר הreverse ומכפילים אותו ב10,
להכפיל מספר ב10 בעצם מקדם את כל המספר שמאלה:
23 * 10 = 230
332 * 10 = 3320
ואז אנחנו יכולים לחבר את המספר העשרוני:
3320 + 3 = 3323
לאחר מכן בסוף אנחנו מחלקים בעזרת האופרטור חילוק של המספר השלם, ז”א הפעולה הזאתי תמיד תחזיר לנו מספר שלם ולא חלקי!
כאן אנחנו בעצם משתמשים בזה בשביל להיפטר מהשארית.
אם לא היינו משתמשים אז החילוק היה יוצא ככה:
3233 / 10 = 323.3
3233 // 10 = 323
בסוף אנחנו בודקים אם המספר שייצא זהה למספר המקורי.
1 | def GetStrongness(password): |
הסבר:
אני כבר אומר מראש ניתן לפתור תרגילים כאלו במגוון דרכים, לכם אם קיבלתם ייעוץ או מצאתם פתרון אחר כל הכבוד!
אני בחרתי לממש אותו בדרך הכי נאיבית כדי להראות לכם שלא צריך להתחכם בשביל לפתור בעיה.
מה שעשינו זה השתמשנו באופרטור in על מנת לבדוק אם התו שלנו נמצא במחרוזת של התווים, המספרים או התווים המיוחדים.
ברגע אנחנו מבינים שניתן להשתמש באופרטור הזה בשביל מחרוזות ומערכים אז זה הופך להיות מאוד פשוט!
1 | def CommandWelcome(): |
הסבר:
הדבר הראשון שצריך לשים לב אליו שכל דבר מומש בפונקציה משלו!
ז”א כל פקודה נמצאת בעטיפה.
את הפקודות עם המשתמשים העברנו את המערך כפרמטר על מנת שלשני הפונקציות תהיה גישה לאותו מערך.
הרי אם יוצרים משתמשים בפונקציה מסוימת, לא תהיה גישה למשתנה הזה בפונקציה אחרת, לכן צריך להעביר אותו כפרמטר.
זוכרים את עקרון הקלט-פלט?
בעדיפות לקבל כמה שיותר ערכים כפרמטרים ולהוציא ערך מתאים.
אם אתם מוצאים שהפונקציה שלכם גדולה מדי או אתם כותבים יותר מדי ערכים, יהיה לזה פתרון אחר שנלמד בהמשך
עד אז עדיף פשוט לחלק לפונקציות קטנות יותר!
בפרק הזה למדנו על הבלוק הבסיסי ביותר בתכנות - הפונקציות.
בפרק הבא נרכיב פונקציות אחת על השנייה בעזרת רקורסיות!