פונקציות לעומק: ערכי ברירת מחדל, Functions as Objects, *args, **kwargs ו-Lambda
- להגדיר פרמטרים עם ערך ברירת מחדל ולקרוא לפונקציה עם ארגומנטים בשם
- להבין שפונקציה היא אובייקט — אפשר לשמור אותה במשתנה ולהעביר אותה לפונקציה אחרת
- לכתוב פונקציות שמקבלות מספר משתנה של ארגומנטים עם
*argsו-**kwargs - לכתוב lambda פשוטה ולהשתמש בה עם
sorted(key=...)
מה נלמד?
בנושא Python מקצועי, קוד נקי ו-Debugging למדנו את הבסיס: def, פרמטרים, return. היום נלמד כמה יכולות שהופכות פונקציות לגמישות הרבה יותר:
- ערכי ברירת מחדל וארגומנטים בשם.
- Functions as Objects — פונקציה היא ערך כמו כל ערך אחר.
*args— כמה ארגומנטים רגילים שרוצים.**kwargs— כמה ארגומנטים בשם שרוצים.- Lambda — פונקציה קצרה בלי שם.
למה צריך את זה?
print("a", "b", "c", sep="-")מקבלת כמה ארגומנטים שרוצים, ועוד ארגומנט בשם. איך היא בנויה? עם*args.sorted(students, key=...)מקבלת פונקציה כפרמטר, כדי לדעת לפי מה למיין.- FastAPI (נושא Backend ומסדי נתונים) בנוי כולו על הרעיון שפונקציה היא אובייקט: כותבים פונקציה, ו-FastAPI “רושם” אותה כ-Endpoint ומפעיל אותה כשמגיעה בקשה.
הסבר מהבסיס
ערכי ברירת מחדל (Default Values)
אפשר לתת לפרמטר ערך שישמש אם לא העבירו ארגומנט:
def greet(name, greeting="Hello"):
return greeting + ", " + namegreet("Dana") → "Hello, Dana". greet("Dana", "Hi") → "Hi, Dana".
כלל: פרמטרים עם ברירת מחדל באים אחרי פרמטרים בלי.
ארגומנטים בשם (Keyword Arguments)
בקריאה אפשר לציין שם של פרמטר, והסדר כבר לא משנה:
greet(greeting="Hey", name="Avi")זה מאוד קריא כשיש הרבה פרמטרים: create_user("dana", age=24, is_admin=False).
Functions as Objects
ב-Python, פונקציה היא אובייקט, כמו מספר או רשימה. לכן אפשר:
- לשמור אותה במשתנה:
say = greet. - לשים אותה ברשימה או ב-dict.
- להעביר אותה כארגומנט לפונקציה אחרת.
ההבדל הקריטי: greet (בלי סוגריים) = הפונקציה עצמה. greet("Dana") (עם סוגריים) = הפעלה, והתוצאה שלה.
greet הוא המתכון: אפשר לצלם אותו ולשלוח לחבר. greet("Dana") הוא העוגה: התוצאה של הכנה לפי המתכון. כשמעבירים פונקציה לפונקציה אחרת, שולחים את המתכון, כדי שהיא תכין לפיו מתי שהיא צריכה.
*args — מספר משתנה של ארגומנטים
כוכבית לפני שם הפרמטר אומרת: “אסוף את כל הארגומנטים הרגילים (לפי מיקום) לתוך tuple”:
def total(*numbers):
return sum(numbers)
total(1, 2) # numbers = (1, 2)
total(5, 10, 20, 1) # numbers = (5, 10, 20, 1)השם args הוא מוסכמה. הכוכבית היא שעושה את העבודה.
**kwargs — מספר משתנה של ארגומנטים בשם
שתי כוכביות: “אסוף את כל הארגומנטים בשם לתוך dict”:
def describe(**details):
for key, value in details.items():
print(key, "=", value)
describe(name="Dana", age=24, city="Haifa")
# details = {"name": "Dana", "age": 24, "city": "Haifa"}kwargs = keyword arguments.
Lambda
lambda היא דרך לכתוב פונקציה קטנה, בשורה אחת, בלי שם:
lambda x: x * 2שקול ל:
def double(x):
return x * 2- אחרי
lambda: הפרמטרים. אחרי:: ביטוי אחד שהוא הערך המוחזר (בליreturn). - השימוש העיקרי: להעביר פונקציה קצרה כארגומנט, במקום להגדיר
defנפרד. בעיקר עםsorted,min,max.
תחביר
# default value + keyword arguments
def func(required, optional="default"):
...
func("a")
func("a", optional="b")
# function as an object
f = func # no parentheses: the function itself
f("a") # call it through the new name
def apply(operation, value):
return operation(value) # call the function we received
# *args and **kwargs
def func(*args, **kwargs):
# args -> tuple of positional arguments
# kwargs -> dict of keyword arguments
...
# lambda
lambda parameters: expression
sorted(items, key=lambda item: item["price"])דוגמת קוד ראשונה
week-2/functions_deep.py:
def make_greeting(name, greeting="Hello", punctuation="!"):
return greeting + ", " + name + punctuation
print(make_greeting("Dana"))
print(make_greeting("Avi", "Good morning"))
print(make_greeting("Noa", punctuation="?"))
def shout(text):
return text.upper()
def whisper(text):
return text.lower() + "..."
def say(message, style):
return style(message)
print(say("Hello there", shout))
print(say("Hello there", whisper))פלט:
Hello, Dana!
Good morning, Avi!
Hello, Noa?
HELLO THERE
hello there...פירוק הקוד שורה אחר שורה
| שורה | הסבר |
|---|---|
def make_greeting(name, greeting="Hello", punctuation="!") | name חובה. השניים האחרים אופציונליים, עם ברירות מחדל. |
make_greeting("Dana") | שני האופציונליים לוקחים ברירת מחדל. |
make_greeting("Avi", "Good morning") | ארגומנט שני לפי מיקום נכנס ל-greeting. |
make_greeting("Noa", punctuation="?") | ארגומנט בשם: מדלגים על greeting (ברירת מחדל) וקובעים רק את punctuation. |
def say(message, style): | style הוא פרמטר שמצפה לפונקציה. |
return style(message) | מפעילים את הפונקציה שהתקבלה. say לא יודעת ולא אכפת לה איזו פונקציה זו. |
say("Hello there", shout) | מעבירים את shout בלי סוגריים, כלומר את הפונקציה עצמה. |
דוגמה נוספת
*args, **kwargs ו-lambda במערכת אמיתית קטנה. week-2/store_tools.py:
def order_total(*prices, discount=0):
subtotal = sum(prices)
return subtotal - subtotal * discount / 100
print(order_total(10, 20, 30))
print(order_total(100, 50, discount=10))
def build_user(username, **extra):
user = {"username": username}
for key, value in extra.items():
user[key] = value
return user
print(build_user("dana"))
print(build_user("avi", age=30, city="Haifa", is_admin=True))
products = [
{"name": "Laptop", "price": 3500},
{"name": "Mouse", "price": 80},
{"name": "Desk", "price": 900},
]
by_price = sorted(products, key=lambda product: product["price"])
for product in by_price:
print(product["name"], product["price"])
cheapest = min(products, key=lambda product: product["price"])
print("Cheapest:", cheapest["name"])פלט:
60.0
135.0
{'username': 'dana'}
{'username': 'avi', 'age': 30, 'city': 'Haifa', 'is_admin': True}
Mouse 80
Desk 900
Laptop 3500
Cheapest: Mouseפירוק:
def order_total(*prices, discount=0):—*pricesאוסף כל מספר של מחירים ל-tuple.discountמופיע אחרי*prices, ולכן אפשר להעביר אותו רק בשם (discount=10). אחרת, Python לא תדע איפה נגמרים המחירים.build_user("avi", age=30, ...)—usernameמקבל"avi", וכל השאר נאסף ל-extraכ-dict:{"age": 30, "city": "Haifa", "is_admin": True}.sorted(products, key=lambda product: product["price"])—sortedלא יודעת איך להשוות בין dicts. ה-keyהוא פונקציה שאומרת לה: “לכל מוצר, תשתמשי במחיר שלו כדי להשוות”. ה-lambda נקראת פעם אחת לכל מוצר.min(..., key=...)— אותו רעיון: “המינימלי לפי מחיר”. מחזיר את המוצר כולו, ולא רק את המחיר.
טעות נפוצה
הטעות 1: להעביר תוצאה במקום פונקציה.
print(say("Hello", shout()))TypeError: shout() missing 1 required positional argument: 'text'shout() עם סוגריים מנסה להפעיל את shout מיד, בלי ארגומנט. כשמעבירים פונקציה, כותבים את השם בלבד: shout.
הטעות 2: רשימה או dict כערך ברירת מחדל.
def add_item(item, cart=[]):
cart.append(item)
return cart
print(add_item("milk"))
print(add_item("bread"))['milk']
['milk', 'bread']ציפינו ל-['bread'] בקריאה השנייה! ערך ברירת המחדל [] נוצר פעם אחת, כשהפונקציה מוגדרת, וכל הקריאות חולקות את אותה רשימה (Mutable, השיעור הקודם!). התיקון המקובל:
def add_item(item, cart=None):
if cart is None:
cart = []
cart.append(item)
return cartהטעות 3: lambda מסובכת מדי.
result = sorted(users, key=lambda u: (0 if u["active"] else 1, -u["score"], u["name"].lower()))זה עובד, אבל אף אחד לא יבין את זה בעוד שבוע (KISS!). אם lambda לא קריאה במבט, כותבים def עם שם ברור.
הטעות 4: פרמטר עם ברירת מחדל לפני פרמטר חובה.
def greet(greeting="Hello", name):SyntaxError: parameter without a default follows parameter with a defaultתרגול
התרגילים מבוססים רק על מה שנלמד בשיעור הזה ובשיעורים שלפניו. נסו לבד, ורק אז פתחו את הפתרון.
תרגיל 1: ברירת מחדל וארגומנטים בשם
כתבו פונקציה make_greeting(name, greeting="Hello", punct="!").
print(make_greeting("Dana"))
print(make_greeting("Avi", "Good morning"))
print(make_greeting("Noa", punct="?"))פלט צפוי:
Hello, Dana!
Good morning, Avi!
Hello, Noa?הצג פתרון
def make_greeting(name, greeting="Hello", punct="!"):
return greeting + ", " + name + punct
print(make_greeting("Dana"))
print(make_greeting("Avi", "Good morning"))
print(make_greeting("Noa", punct="?"))Hello, Dana!
Good morning, Avi!
Hello, Noa?בקריאה השלישית punct="?" עובר לפי שם, ולכן אפשר לדלג על greeting ולהשאיר לו את ברירת המחדל.
תרגיל 2: *args
כתבו פונקציה average(*numbers) שמקבלת כל כמות מספרים ומחזירה את הממוצע. אם לא הועבר אף מספר — היא זורקת ValueError עם ההודעה שבפלט הצפוי.
print(average(80, 90))
print(average(70, 80, 90, 100))
try:
average()
except ValueError as error:
print("error:", error)פלט צפוי:
85.0
85.0
error: need at least one numberהצג פתרון
def average(*numbers):
if len(numbers) == 0:
raise ValueError("need at least one number")
return sum(numbers) / len(numbers)
print(average(80, 90))
print(average(70, 80, 90, 100))
try:
average()
except ValueError as error:
print("error:", error)85.0
85.0
error: need at least one number*numbers אוסף את כל הארגומנטים לפי מיקום ל-tuple.
תרגיל 3: **kwargs
כתבו פונקציה format_profile(name, **details) שמחזירה שורה עם השם, ואחריו כל פרט נוסף בפורמט key=value.
print(format_profile("Dana", age=24, city="Haifa"))
print(format_profile("Avi"))פלט צפוי:
Dana: age=24 city=Haifa
Avi:הצג פתרון
def format_profile(name, **details):
line = name + ":"
for key, value in details.items():
line = line + " " + key + "=" + str(value)
return line
print(format_profile("Dana", age=24, city="Haifa"))
print(format_profile("Avi"))Dana: age=24 city=Haifa
Avi:**details אוסף את כל הארגומנטים בשם ל-dict.
תרגיל 4: lambda במיון
products = [("Laptop", 3500), ("Mouse", 80), ("Desk", 900)]- מיינו לפי המחיר, מהזול ליקר, והדפיסו כל מוצר.
- הדפיסו את שם המוצר היקר ביותר.
פלט צפוי:
Mouse 80
Desk 900
Laptop 3500
Most expensive: Laptopהצג פתרון
products = [("Laptop", 3500), ("Mouse", 80), ("Desk", 900)]
for name, price in sorted(products, key=lambda p: p[1]):
print(name, price)
most_expensive = max(products, key=lambda p: p[1])
print("Most expensive:", most_expensive[0])Mouse 80
Desk 900
Laptop 3500
Most expensive: Laptoplambda p: p[1] היא פונקציה קצרה שמחזירה את המחיר — sorted ו-max משווים לפיה.
תרגיל 5: ברירת מחדל משתנה
def add_item(item, cart=[]):
cart.append(item)
return cart
print(add_item("milk"))
print(add_item("bread"))- מה יודפס בקריאה השנייה, ולמה?
- תקנו את הפונקציה.
הצג פתרון
- יודפס
['milk', 'bread']. ברירת המחדל[]נוצרת פעם אחת, כשהפונקציה מוגדרת — וכל הקריאות חולקות את אותה רשימה. - משתמשים ב-
None, ויוצרים רשימה חדשה בתוך הפונקציה:
def add_item(item, cart=None):
if cart is None:
cart = []
cart.append(item)
return cart
print(add_item("milk"))
print(add_item("bread"))['milk']
['bread']סיכום
- ברירת מחדל:
def f(a, b=10). פרמטרים עם ברירת מחדל באים אחרונים. לעולם לא[]או{}כברירת מחדל (משתמשים ב-None). - ארגומנט בשם:
f(a=1, b=2). קריא, והסדר לא משנה. - פונקציה היא אובייקט:
f= הפונקציה,f()= הפעלה. אפשר לשמור אותה במשתנה, ב-dict, ולהעביר כארגומנט. *argsאוסף ארגומנטים רגילים ל-tuple.**kwargsאוסף ארגומנטים בשם ל-dict.lambda x: expressionהיא פונקציה קצרה בלי שם. מושלמת ל-key=שלsorted/min/max. אם היא מסובכת, עדיףdef.
דוקומנטציה רשמית
- Default Argument Values — ערכי ברירת מחדל
- Arbitrary Argument Lists — *args
- Keyword Arguments — **kwargs
- Lambda Expressions — lambda