פייתון/פייתון גרסה 2/פונקציות

מתוך ויקיספר, אוסף הספרים והמדריכים החופשי

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

הצורך בפונקציות[עריכה]

נניח שאנו כותבים תוכנית קטנה להמרת מעלות מ-Celsius ל-Fahrenheit (ראה גם כאן וכאן). התכנית תתחיל בכך שתדפיס את התרגום למעלות Fahrenheit של מעלות ה-Celsius בערכים 0, 4, 8, ..., 40, ולאחר מכן תבקש מהמשתמש מעלה ב-Celsius, ותדפיס את ערכו ב-Fahrenheit.

נרשום את התוכנית כך:

for c in range(0, 40, 4):
    f = 1.8 * c + 32
    
    print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

c = raw_input('Enter degrees in Clesius: ')

f = 1.8 * c + 32

print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

נוכל לשים לב שהשורה

f = 1.8 * c + 32

מופיעה פעמיים בתוכנית. זהו דבר בעייתי:

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

ככל שהתוכנית ארוכה ומסובכת יותר, הבעייתיות בדברים כאלה גדלה.

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

def celsius_to_fahrenheit(celsius):
    return 1.8 * celsius + 32

הגדרת פונקציה[עריכה]

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

def <function_name>(<arguments>):
    <body>

כאשר:

  • function_name הוא שם הפונקציה.
  • arguments הם ארגומנטים, כלומר משתנים שערכם נקבע מחוץ לפונקציה.
  • body הוא הפקודות המתבצעות כשהפונקציה נקראת.

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

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

return <value>

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

return

דבר שיגרום ליציאה מהפונקציה.

דוגמאות[עריכה]

פונקציה עם ערך מוחזר[עריכה]

הנה הפונקציה הממירה מספר נקודה-צפה המתאר טמפרטורה ב-Celsius לטמפרטורה ב-Fahrenheit:

def celsius_to_fahrenheit(celsius):
    return 1.8 * celsius + 32

הפונקציה מקבלת משתנה מסוג מספר נקודה-צפה ששמו celsius, ומחזירה מספר נקודה-צפה.

פונקציה בלי ערך מוחזר[עריכה]

הנה פונקציה המדפיסה את התרגום למעלות Fahrenheit של מעלות הCelsius בערכים 0, 4, 8, ..., 40:

def print_conversion_table():
    for c in range(0, 40, 4):
        f = 1.8 * c + 32
  
        print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

פונקציה זו איננה מקבלת אף פרמטר, ו(בלי שום קשר) גם אינה מחזירה אף ערך.


פונקציה בלי ערך מוחזר ופקודת יציאה מפורשת[עריכה]

נניח שהחלטנו לשאול את המשתמש האם להדפיס את טבלת ההמרות, ואם המשתמש יקליד את התו 'n', לא נדפיס כלום.. נוכל לכתוב זאת כך:

def print_conversion_table_if_needed():
    reply = raw_input('Print out conversion table?')
    if reply == 'n':
        return
    for c in range(0, 40, 4):
        f = 1.8 * c + 32
        print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

בפונקציה זו, נשים לב לשורות:

if reply == 'n':
    return

הפקודה return גורמת ליציאה מהפונקציה (ומחזירה None). אם הפקודה מתבצעת, אז שאר הפקודות עד סוף הפונקציה אינן מתבצעות.

קריאה לפונקציה[עריכה]

קריאה לפונקציה נכתבת כך:

<function_name>(<values>)

כאשר function_name היא שם הפונקציה, ו-values הם הערכים שיש להשים למשתניה. אם הפונקציה אינה מקבלת ארגומנטים, פשוט רושמים כך:

<function_name>()

.

להלן דוגמה לקריאה לפונקציה celsius_to_fahrenheit:

def celsius_to_fahrenheit(celsius):
    return 1.8 * celsius + 32


f = celsius_to_fahreneit(3)

print f

השורה

f = celsius_to_fahreneit(3)

קוראת לפונקציה עם הערך 3. כעת הפונקציה מתחילה לפעול, ובתוך הפוקנציה, המשתנה celsius הוא בעל הערך 3. כשהפונקציה מגיעה לשורה

return 1.8 * celsius + 32

חוזר רצף התוכנית לשורה שקראה לה. במקרה זה, הערך המוחזר מהפונקציה יושם למשתנה f.

אם נחזור שוב לתוכנית המקורית שרשמנו בתחילת הפרק, נוכל לכתוב אותה כך:

def celsius_to_fahrenheit(celsius):
    return 1.8 * celsius + 32


for c in range(0, 40, 4):
    f = celsius_to_fahrenheit(c)
    
    print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

c = raw_input('Enter degrees in Clesius: ')

f = celsius_to_fahrenheit(c)

print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

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

def celsius_to_fahrenheit(celsius):
    return 1.8 * celsius + 32


for c in range(0, 40, 4):
    print str(c) + ' in Celsius is ' + str( celsius_to_fahrenheit(c) ) + ' in Fahrenheit'

c = raw_input('Enter degrees in Clesius: ')

print str(c) + ' in Celsius is ' + str( celsius_to_fahrenheit(c) ) + ' in Fahrenheit'


פונקציות רקורסיביות[עריכה]

שקלו לדלג על נושא זה

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



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

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

def factorial(n):
    fact = 1
    
    for i in range(1, n+1):
        fact = fact * i
        
    return fact

ולהלן פונקציה רקורסיבית לחישוב עצרת:

def factorial(n):
    if n == 0:
        return 1
    
    return n * factorial(n - 1)

או בצורה קצרה יותר:

def factorial(n):
    return 1 if n == 0 else n * factorial(n - 1)

מעט על פונקציות והנדסת תוכנה[עריכה]

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

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

נשתמש בקוד שראינו בצורך בפונקציות כדוגמה (למרות שזהו קוד פשוט מאד):

for c in range(0, 40, 4):
    f = 1.8 * c + 32
    
    print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

c = int (raw_input('Enter degrees in Clesius: '))

f = 1.8 * c + 32

print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

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

def print_init_conversion_table():
    for c in range(0, 40, 4):
        f = 1.8 * c + 32    
        print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

def handle_conversion_query():
    c = int (raw_input('Enter degrees in Clesius: '))
    f = 1.8 * c + 32
    print str(c) + ' in Celsius is ' + str(f) + ' in Fahrenheit'

print_init_conversion_table()
handle_conversion_query()

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

def celsius_to_fahrenheit(celsius):
    return 1.8 * celsius + 32
        
def print_init_conversion_table():
    for c in range(0, 40, 4):
        print str(c) + ' in Celsius is ' + str( celsius_to_fahrenheit(c) ) + ' in Fahrenheit'

def handle_conversion_query():
    c = int (raw_input('Enter degrees in Clesius: '))
    print str(c) + ' in Celsius is ' + str( celsius_to_fahrenheit(c) ) + ' in Fahrenheit'

print_init_conversion_table()
handle_conversion_query()

איכות הקוד כעת טובה יותר:

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


- פונקציות -