יאללה תעמיסו
המושג העמסת פונקציות מתיחס ליכולת של שפות תכנות מסוימות להדביק מספר מימושים לכל שם של פונקציה, ולהחליט בזמן הקומפילציה לפי החתימה איזה מימוש להפעיל בכל סיטואציה. בשפות דינמיות אין קומפיילר ולכן המושג המקביל בשפות כמו רובי או פייתון הוא Multiple Dispatching. גם המימוש קצת שונה, ומנגנון Multiple Dispatching עובד על הרעיון שבזמן ריצה אנחנו מסתכלים איזה פרמטרים פונקציה קיבלה ולפי זה מחליטים איזה מימוש שלה להפעיל.
אם ניקח את Python כדוגמא נוכל לדמיין את הקוד הבא שמגדיר שני מימושים לאותה פונקציה:
@dispatch(tuple)
def go(point):
x, y = point
go(x, y)
@dispatch(int, int)
def go(x, y):
print(f'x = {x}, y = {y}')
כאשר כל אחד מהמימושים אחראי על סט פרמטרים אחר. נרצה שהראשון ייקרא אם נפעיל את הפונקציה עם פרמטר יחיד:
loc = (10, 10)
go(loc)
ונרצה שהשני ייקרא כשנפעיל את הפונקציה עם שני פרמטרים:
go(10, 10)
נשמע לכם דמיוני? מסתבר שכל מה שצריך בשביל לגרום לקסם הזה לעבוד הוא לכתוב את ה Decorator. המפתח למימוש ה Decorator הוא לשמור מילון של מילונים, בו שם של פונקציה הוא המפתח הראשי, ואז לכל פונקציה אנחנו שומרים מילון שהמפתח שלו הוא סט פרמטרים מסוים והערך הוא הגדרה מסוימת של הפונקציה. במקרה שלנו המילון הגדול יראה כך:
{
go: {
(typle): <function at 0x1088b01e0>
(int, int): <function at 0x1089d1488>
}
}
הפונקציה go עצמה שהתוכנית מפעילה, כלומר זו שה Decorator מחזיר תסתכל ברשימת הפרמטרים שקיבלה, תלך למילון לחפש שם מפתח שמתאים לסוגים שלהם ולפי זה תחליט לאיזה פונקציה לקרוא.
מימוש פשוט של המנגנון יכול להיראות כך:
import collections
md = collections.defaultdict(dict)
def dispatch(*types):
def decorator(f):
md[f.__name__][types] = f
def wrapper(*args, **kwargs):
res = md[f.__name__][tuple(type(a) for a in args)]
res(*args, **kwargs)
return wrapper
return decorator
ואם אתם בעניין של מימוש יותר רציני שגם נבדק בעולם האמיתי וכולל די הרבה אופטימיזציות שווה לבדוק את הספריה multipledispatch. זה דף הבית שלהם: