Skip to content

אוקיי, הנה התרגום של המאמר לעברית:


תוכן עניינים

  • העברת ארגומנטים מרובים לפונקציה
  • שימוש במשתנה args של פייתון בהגדרות פונקציה
  • שימוש במשתנה kwargs של פייתון בהגדרות פונקציה
  • סדר הארגומנטים בפונקציה
  • פריסה (Unpacking) עם אופרטורי הכוכבית: * ו-**
  • סיכום

[הסר פרסומות]
[צפה כעת] למדריך זה יש קורס וידאו קשור שנוצר על ידי צוות Real Python. צפה בו יחד עם המדריך הכתוב כדי להעמיק את הבנתך: Python args ו-kwargs: פיענוח

לפעמים, כשמסתכלים על הגדרת פונקציה בפייתון, ייתכן שתראו שהיא מקבלת שני ארגומנטים מוזרים: *args ו-**kwargs. אם אי פעם תהיתם מהם המשתנים המוזרים האלה, או מדוע סביבת הפיתוח (IDE) שלכם מגדירה אותם ב-main(), אז המאמר הזה בשבילכם. תלמדו כיצד להשתמש ב-args ו-kwargs בפייתון כדי להוסיף גמישות רבה יותר לפונקצי обыכם.

בסוף המאמר, תדעו:

  • מה *args ו-**kwargs באמת אומרים
  • כיצד להשתמש ב-*args ו-**kwargs בהגדרות פונקציה
  • כיצד להשתמש בכוכבית בודדת (*) כדי לפרוס אובייקטים איטרביליים (iterables)
  • כיצד להשתמש בשתי כוכביות (**) כדי לפרוס מילונים (dictionaries)

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

בונוס חינם: לחצו כאן כדי לקבל דף נוסחאות (Cheat Sheet) של פייתון וללמוד את יסודות פייתון 3, כמו עבודה עם סוגי נתונים, מילונים, רשימות ופונקציות פייתון.

קחו את הבוחן: בחנו את הידע שלכם עם הבוחן האינטראקטיבי שלנו "Python args ו-kwargs: פיענוח". תקבלו ציון בסיום כדי לעזור לכם לעקוב אחר התקדמות הלמידה שלכם:

Python args ו-kwargs: פיענוח
בוחן אינטראקטיבי

Python args ו-kwargs: פיענוח
בבוחן זה, תבדקו את הבנתכם כיצד להשתמש ב-*args ו-**kwargs בפייתון. עם ידע זה, תוכלו להוסיף גמישות רבה יותר לפונקציות שלכם.

העברת ארגומנטים מרובים לפונקציה
*args ו-**kwargs מאפשרים לכם להעביר ארגומנטים מרובים או ארגומנטים עם מילות מפתח (keyword arguments) לפונקציה. שקלו את הדוגמה הבאה. זוהי פונקציה פשוטה שמקבלת שני ארגומנטים ומחזירה את סכומם:

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

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

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

sum_integers_list.py

def my_sum(my_integers):
    result = 0
    for x in my_integers:
        result += x
    return result

list_of_integers = [1, 2, 3]
print(my_sum(list_of_integers))

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

כאן *args יכול להיות שימושי מאוד, מכיוון שהוא מאפשר לכם להעביר מספר משתנה של ארגומנטים מיקומיים (positional arguments). קחו את הדוגמה הבאה:

sum_integers_args.py

def my_sum(*args):
    result = 0
    # איטרציה על הטאפל args של פייתון
    for x in args:
        result += x
    return result

print(my_sum(1, 2, 3))

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

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

sum_integers_args_2.py

def my_sum(*integers):
    result = 0
    for x in integers:
        result += x
    return result

print(my_sum(1, 2, 3))

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

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

change_list.py

my_list = [1, 2, 3]
my_list[0] = 9
print(my_list)

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

$ python change_list.py
[9, 2, 3]

הערך הראשון כבר אינו 0, אלא הערך המעודכן 9. כעת, נסו לעשות את אותו הדבר עם טאפל:

change_tuple.py

my_tuple = (1, 2, 3)
my_tuple[0] = 9
print(my_tuple)

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

$ python change_tuple.py
Traceback (most recent call last):
  File "change_tuple.py", line 3, in <module>
    my_tuple[0] = 9
TypeError: 'tuple' object does not support item assignment

זאת מכיוון שטאפל הוא אובייקט בלתי ניתן לשינוי (immutable), ולא ניתן לשנות את ערכיו לאחר ההקצאה. זכרו זאת כשאתם עובדים עם טאפלים ו-*args.

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

concatenate.py

def concatenate(**kwargs):
    result = ""
    # איטרציה על מילון ה-kwargs של פייתון
    for arg in kwargs.values():
        result += arg
    return result

print(concatenate(a="Real", b="Python", c="Is", d="Great", e="!"))

כאשר תריצו את הסקריפט הנ"ל, concatenate() יעבור באיטרציה על מילון ה-kwargs של פייתון וישרשר את כל הערכים שהוא מוצא:

$ python concatenate.py
RealPythonIsGreat!

כמו args, גם kwargs הוא רק שם שניתן לשנות לכל מה שתרצו. שוב, מה שחשוב כאן הוא השימוש באופרטור הפריסה (**).

לכן, את הדוגמה הקודמת ניתן היה לכתוב כך:

concatenate_2.py

def concatenate(**words):
    result = ""
    for arg in words.values():
        result += arg
    return result

print(concatenate(a="Real", b="Python", c="Is", d="Great", e="!"))

שימו לב שבדוגמה הנ"ל האובייקט האיטרבילי הוא dict סטנדרטי. אם אתם עוברים באיטרציה על המילון ורוצים להחזיר את ערכיו, כמו בדוגמה המוצגת, עליכם להשתמש ב-.values().

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

concatenate_keys.py

def concatenate(**kwargs):
    result = ""
    # איטרציה על המפתחות של מילון ה-kwargs של פייתון
    for arg in kwargs:
        result += arg
    return result

print(concatenate(a="Real", b="Python", c="Is", d="Great", e="!"))

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

$ python concatenate_keys.py
abcde

כפי שאתם רואים, אם אינכם מציינים .values(), הפונקציה שלכם תעבור באיטרציה על המפתחות של מילון ה-kwargs שלכם, ותחזיר את התוצאה השגויה.

[הסר פרסומות]
סדר הארגומנטים בפונקציה
עכשיו שלמדתם למה *args ו-**kwargs מיועדים, אתם מוכנים להתחיל לכתוב פונקציות שמקבלות מספר משתנה של ארגומנטים כקלט. אך מה אם ברצונכם ליצור פונקציה שמקבלת מספר משתנה של ארגומנטים מיקומיים וגם ארגומנטים עם שם?

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

לסיכום, הסדר הנכון לפרמטרים שלכם הוא:

  1. ארגומנטים סטנדרטיים
  2. ארגומנטים של *args
  3. ארגומנטים של **kwargs

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

correct_function_definition.py

def my_function(a, b, *args, **kwargs):
    pass

המשתנה *args מופיע כראוי לפני **kwargs. אך מה אם תנסו לשנות את סדר הארגומנטים? לדוגמה, שקלו את הפונקציה הבאה:

wrong_function_definition.py

def my_function(a, b, **kwargs, *args):
    pass

כעת, **kwargs מופיע לפני *args בהגדרת הפונקציה. אם תנסו להריץ דוגמה זו, תקבלו שגיאה מהמפרשן:

$ python wrong_function_definition.py
  File "wrong_function_definition.py", line 2
    def my_function(a, b, **kwargs, *args):
                                    ^
SyntaxError: invalid syntax

במקרה זה, מכיוון ש-*args מופיע אחרי **kwargs, מפרשן הפייתון זורק SyntaxError.

פריסה (Unpacking) עם אופרטורי הכוכבית: * ו-
כעת אתם מסוגלים להשתמש ב-*args ו-**kwargs כדי להגדיר פונקציות פייתון המקבלות מספר משתנה של ארגומנטים כקלט. בואו נצלול קצת יותר לעומק כדי להבין משהו נוסף על אופרטורי הפריסה.

אופרטורי הפריסה עם כוכבית בודדת וכפולה הוצגו בפייתון 2. החל מגרסה 3.5, הם הפכו לעוצמתיים עוד יותר, הודות ל-PEP 448. בקצרה, אופרטורי הפריסה הם אופרטורים שפורסים את הערכים מאובייקטים איטרביליים בפייתון. ניתן להשתמש באופרטור הכוכבית הבודדת () על כל אובייקט איטרבילי שפייתון מספקת, בעוד שבאופרטור הכוכבית הכפולה (*) ניתן להשתמש רק על מילונים.

נתחיל עם דוגמה:

print_list.py

my_list = [1, 2, 3]
print(my_list)

קוד זה מגדיר רשימה ואז מדפיס אותה לפלט הסטנדרטי:

$ python print_list.py
[1, 2, 3]

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

כעת, נסו להוסיף את אופרטור הפריסה * לפני שם הרשימה שלכם:

print_unpacked_list.py

my_list = [1, 2, 3]
print(*my_list)

כאן, האופרטור * אומר ל-print() לפרוס את הרשימה תחילה.

במקרה זה, הפלט הוא כבר לא הרשימה עצמה, אלא התוכן של הרשימה:

$ python print_unpacked_list.py
1 2 3

האם אתם רואים את ההבדל בין הרצה זו לזו מ-print_list.py? במקום רשימה, print() קיבלה שלושה ארגומנטים נפרדים כקלט.

דבר נוסף שתבחינו בו הוא שב-print_unpacked_list.py, השתמשתם באופרטור הפריסה * כדי לקרוא לפונקציה, במקום בהגדרת פונקציה. במקרה זה, print() לוקחת את כל הפריטים של הרשימה כאילו היו ארגומנטים בודדים.

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

כדי לבדוק התנהגות זו, שקלו את הסקריפט הבא:

unpacking_call.py

def my_sum(a, b, c):
    print(a + b + c)

my_list = [1, 2, 3]
my_sum(*my_list)

כאן, my_sum() מציינת במפורש ש-a, b, ו-c הם ארגומנטים נדרשים.

אם תריצו סקריפט זה, תקבלו את הסכום של שלושת המספרים ב-my_list:

$ python unpacking_call.py
6

3 האלמנטים ב-my_list תואמים בצורה מושלמת לארגומנטים הנדרשים ב-my_sum().

כעת הסתכלו על הסקריפט הבא, שבו ל-my_list יש 4 ארגומנטים במקום 3:

wrong_unpacking_call.py

def my_sum(a, b, c):
    print(a + b + c)

my_list = [1, 2, 3, 4]
my_sum(*my_list)

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

$ python wrong_unpacking_call.py
Traceback (most recent call last):
  File "wrong_unpacking_call.py", line 6, in <module>
    my_sum(*my_list)
TypeError: my_sum() takes 3 positional arguments but 4 were given

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

כדי לבדוק התנהגות זו, שקלו את הדוגמה הבאה:

sum_integers_args_3.py

def my_sum(*args):
    result = 0
    for x in args:
        result += x
    return result

list1 = [1, 2, 3]
list2 = [4, 5]
list3 = [6, 7, 8, 9]

print(my_sum(*list1, *list2, *list3))

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

$ python sum_integers_args_3.py
45

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

extract_list_body.py

my_list = [1, 2, 3, 4, 5, 6]

a, *b, c = my_list

print(a)
print(b)
print(c)

בדוגמה זו, my_list מכילה 6 פריטים. המשתנה הראשון מוקצה ל-a, האחרון ל-c, וכל שאר הערכים נארזים לרשימה חדשה b. אם תריצו את הסקריפט, print() יראה לכם שלשלושת המשתנים שלכם יש את הערכים שהייתם מצפים להם:

$ python extract_list_body.py
1
[2, 3, 4, 5]
6

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

merging_lists.py

my_first_list = [1, 2, 3]
my_second_list = [4, 5, 6]
my_merged_list = [*my_first_list, *my_second_list]

print(my_merged_list)

אופרטור הפריסה * מצורף לפני my_first_list וגם לפני my_second_list.

אם תריצו סקריפט זה, תראו שהתוצאה היא רשימה ממוזגת:

$ python merging_lists.py
[1, 2, 3, 4, 5, 6]

אתם יכולים אפילו למזג שני מילונים שונים באמצעות אופרטור הפריסה **:

merging_dicts.py

my_first_dict = {"A": 1, "B": 2}
my_second_dict = {"C": 3, "D": 4}
my_merged_dict = {**my_first_dict, **my_second_dict}

print(my_merged_dict)

כאן, האובייקטים האיטרביליים למיזוג הם my_first_dict ו-my_second_dict.

הפעלת קוד זה מוציאה מילון ממוזג:

$ python merging_dicts.py
{'A': 1, 'B': 2, 'C': 3, 'D': 4}

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

string_to_list.py

a = [*"RealPython"]
print(a)

בפייתון, מחרוזות הן אובייקטים איטרביליים, כך ש-* יפרוס אותה וימקם את כל הערכים הבודדים ברשימה a:

$ python string_to_list.py
['R', 'e', 'a', 'l', 'P', 'y', 't', 'h', 'o', 'n']

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

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

mysterious_statement.py

*a, = "RealPython"
print(a)

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

הפסיק אחרי ה-a עושה את העבודה. כאשר אתם משתמשים באופרטור הפריסה עם הקצאת משתנים, פייתון דורשת שהמשתנה שנוצר יהיה רשימה או טאפל. עם הפסיק הסוגר, הגדרתם טאפל עם משתנה בעל שם אחד בלבד, a, שהוא הרשימה ['R', 'e', 'a', 'l', 'P', 'y', 't', 'h', 'o', 'n'].

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

[הסר פרסומות]
סיכום
כעת אתם מסוגלים להשתמש ב-*args ו-**kwargs כדי לקבל מספר משתנה של ארגומנטים בפונקציות שלכם. למדתם גם משהו נוסף על אופרטורי הפריסה.

למדתם:

  • מה *args ו-**kwargs באמת אומרים
  • כיצד להשתמש ב-*args ו-**kwargs בהגדרות פונקציה
  • כיצד להשתמש בכוכבית בודדת (*) כדי לפרוס אובייקטים איטרביליים
  • כיצד להשתמש בשתי כוכביות (**) כדי לפרוס מילונים

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


כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *