February 7, 2021

פייתון 7 - פונקציות


פרק קודם:

פייתון 6 - לולאות

מה נלמד:

  • שימוש חוזר של קוד
  • מה זה פונקציות, מתודות ומה הן מגדירות
  • איך להגדיר פונקציות בפייתון
  • עקרון האחריות היחידה - Single Responsibility principal

שימוש חוזר של קוד

הקוד הראשון שאי פעם רץ היה מלפני שנים רבות - למען האמת עוד בשנות ה-40!
המתכנתים הראשונים היו בתקופת מלחמת העולם השנייה - אני קורא להם מתכנתים כי הם היו הראשונים שהשתמשו במחשב אלקטרוני!

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

בתכנות קיימים הרבה טכניקות לשימוש חוזר של קוד ושיתוף תבניות - היום נלמד על הדרך הכי פשוטה - פונקציות.

מה הן פונקציות

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

1
2
3
4
5
6
arr = [1,2,3]
tempAvg = 0
for num in arr:
tempAvg +=num
tempAvg = tempAvg / len(arr)
avg = tempAvg

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

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

פונקציה זו קופסה שאנחנו נותנים לה שם, והיא יכולה להוציא פלט או להחליט לא להוציא פלט, והיא יכולה לקבל קלט או לא לקבל קלט זו החלטה של כותב הפונקציה.

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

לקופסה קוראים average, היא מקבלת מערך ומחזירה מספר.

טרמינולוגיה חשובה לדעת

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

  • Average()
  • Sum()
  • BuyItem()
  • Sell()
  • CalculateBalance()
  • Wave()

שמות פחות טובים:

  • Do - מאוד כללי ולא ברור מה הפונקציה עושה
  • Apples - הוא זה עושה משהו עם תפוחים אבל זה ממש לא ברור, אל תבחרו בשמות עצם!
  • DoAndUpdateAndTransform - כאן יש לנו שמות של פעלים אבל הבעיתיות היא שזה עושה מעבר למשהו אחד ומבלבל מאוד את הפונקציה!

ניתן לקרוא לפונקציות בצורה שונה:

  • מתודות - Methods - בד”כ מתייחס לפונקציות שלא מחזירות ערך.
  • פעולות - Actions
  • קריאה - Invocation - מאוד נדיר כמעט לא משתמשים בזה.

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

למשל:

1
2
arr = [1,2,3]
avg = average(arr)

ככה אנחנו קוראים את הקוד:
אתחלנו מערך בשם arr לערכים 1,2 ו3.
לאחר מכן קראנו לפונקציה average עם המערך והשמנו את הערך שלו במשתנה avg.

לפלט וקלט יש גם שמות נוספים,
לקלט אנחנו קוראים פרטמרים - Parameters.
ופלט לעיתים אנחנו אומרים “ערך החזרה” - Return value.
מכיוון שיכול להיות יותר מקלט אחד אבל בד”כ רק פלט אחד.

תרגיל

  1. חשבו על דברים מהחיים שלכם שאתם יכולים להגדיר כקופסה שחורה ותתנו לזה שם של פעולה.
  2. תתארו את הקוד הנ”ל:
1
2
arr = [2,5,6]
print(sum(arr))

פונקציות בפייתון

ראינו כבר דוגמאות לקריאות לפונקציות,
שני הפונקציות הכי נפוצות שהשתמשנו בהן הן print וinput.
לכל אורך המאמר קראתי להן פעולות על מנת לא לבלבל בטרמינולוגיה מתקדמת אך מעתה נקרא להן פונקציות.

התחביר בפייתון

הגדרה של פונקציה נראית כך:

1
2
def NameOfFunction(input1, input2):
pass

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

פורמט שמות שאני משתמש בו רבות הוא UpperCamelCase - כל מילה חדשה מתחילה באות גדולה.
למשל:

1
2
def CalculateSum():
pass

אם אנחנו רוצים יותר מפרמטר אחד אנחנו מפרידים אותם ע”י פסיקים.

כמה דוגמאות:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def Average(array):
tempAvg = 0
for num in array:
tempAvg +=num
tempAvg = tempAvg / len(array)
return tempAvg

arr = [1,2,3]
avg = Average(arr)

def Sum(a, b):
return a + b

def Multiply(a, b):
return a * b

print(Sum(1,2))
print(Multiply(2,3))

על מנת לקרוא לפונקציה היא צריכה להיות מוגדרת לפני הקוד שקורא לה.
תנסו להריץ את הקוד הנ”ל:

1
2
3
4
result = Sum(1,2)

def Sum(a, b):
return a+b

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

1
2
3
4
def printFruit(name, price):
print(f'We sell {name}s for {price} shekels per kg')

a = printFruit('apple', 5)

עקרון האחריות היחידה - Single Responsibility princple

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

דוגמא לקוד לא טוב שעושה כמה דברים:

1
2
3
4
5
6
7
8
9
10
11
12
13
def CalculateResults(arr, a, b, c):
tempAvg = 0
for num in arr:
tempAvg +=num
avg = tempAvg / len(arr)

sum = a + b + c

return [avg, sum]

results = CalculateResults([1,2,3,4], 6,7,8)
print(results[0])
print(results[1])

הקוד הזה מעלה תהיות ושאלות -

  • מה זה Results?
  • האם יש רעיון מאחורי החזרה של מערך?
  • למה a,b,c איננו מערך גם כן?

דוגמא לקוד שמבצע את אותם דברים אך עם העקרון לאחריות יחידה - SRP בקיצור.

1
2
3
4
5
6
7
8
9
10
11
12
13
def CalculateAverage(arr):
tempAvg = 0
for num in arr:
tempAvg +=num
return tempAvg / len(arr)

def CalculateSum(a,b,c):
return a+b+c

avg = CalculateAverage([1,2,3,4])
sum = CalculateSum(6,7,8)
print(avg)
print(sum)

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

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

תרגילים

  1. כתבו פונקציה שמקבלת מחרוזת ומחריזה את המחרוזת ההופכה שלה, למשל:
    “abc” -> “cba”
    זוכרים אינדקס שלילי?

מימשו את פונקציה כך שכל הבדיקות ייצאו חיוביות:

1
2
3
4
5
def Reverse(str):
pass

assert "cba" == Reverse("abc")
assert "123" == Reverse("321")
  1. כתבו פונקציה שבודקת אם המספר הוא פולינדרומי
    דוגמאות למספר פולינדרומי:
  • 1234321
  • 2004664002
1
2
3
4
5
def IsPalindrome(number):
pass

assert IsPalindrome(12321)
assert IsPalindrome(4334)
  1. לתוכנת מסד נתונים נדרשנו לכתוב פונקציה שבודקת אם הסיסמא שהכנסו היא חזקה,
    סיסמא חלשה מכילה מספרים בלבד
    סיסמא בינונית מכילה מספרים ואותיות,
    סיסמא חזקה מכילה אותיות, מספרים וסימנים מיוחדים.
    למשל:
  • חלש - 123456
  • בינוני - a123bcd
  • חזק - 9t7#aa

כרגע אתם יכולים לפתח פתרון נאיבי - יש מגוון צורות לפתור את זה.

  1. כתבו תוכנה שמבצעת פקודות ע”פ הקלט של המשתמש.
    התוכנה תעבוד בלולאה, ועל כל קלט הוא ייבדוק אם קיימת פקודה שהמשתמש הקיש.
    פקודות למימוש:
  • “hello” -> התוכנה תדפיס “Welcome”.
  • “goodbye” -> התוכנה תדפיס “See you again” ותצא
  • “sum” -> הפקודה תבקש שני מספרים מהמשתמש ותדפיס את התוצאה שלהן.
  • “register” -> הפקודה תבקש שם משתמש ותזכור את המשתמש כך שאם מנסים להירשם עם אותו שם משתמש זה מדפיס לו שכבר קיים.
  • “find” -> הפקודה תבקש שם משתמש ותבדוק אם קיים במערכת.




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

פייתון 8 - רקורסיה

על הפוסט

הפוסט נכתב על ידי Ilya, רישיון על ידי CC BY-NC-ND 4.0.

שתפו את הפוסט

Email Facebook Linkedin Print

קנו לי קפה

#Software#Python