פונקציות
בפייתון, פונקציות הן אובייקטים מהשורה הראשונה (first-class objects). המשמעות היא שאפשר:
- לשמור פונקציות במשתנים
- להעביר פונקציות כפרמטרים לפונקציות אחרות
- להחזיר פונקציות מתוך פונקציות
- ואפילו להגדיר פונקציות בתוך פונקציות
היכולת הזו מאפשרת להשתמש בתבניות מתקדמות כמו פונקציות מסדר גבוה (higher-order functions) ודקורטורים.
דקורטורים
דקורטורים בפייתון הם כלי ברמה גבוהה שמאפשר לשנות או להרחיב התנהגות של פונקציות (או מחלקות) – מבלי לשנות את הקוד המקורי שלהן.
דקורטור הוא פשוט אובייקט שניתן לקריאה (בדרך כלל פונקציה) שמקבל פונקציה כקלט, ומחזיר פונקציה חדשה (או אובייקט שמתנהג כמו פונקציה).
השימוש בתחביר @decorator_name לפני הגדרת הפונקציה הוא קיצור תחבירי שמחיל את הדקורטור על הפונקציה.
לדוגמה, הנה דקורטור פשוט שמדפיס הודעה לפני ואחרי קריאה לפונקציה:
בקטע הקוד הזה, log calls הוא דקורטור. אנחנו מחילים אותו על הפונקציה add באמצעות התחביר @log calls. הפונקציה הפנימית wrapper "עוטפת" את הפונקציה המקורית func, ומוסיפה הדפסות לפני ואחרי ההרצה שלה. בצורה זו, מבלי לשנות את הקוד של add, הוספנו לה התנהגות של רישום (logging).
דקורטורים משמשים לרוב לטיפול בדאגות רוחביות (cross-cutting concerns) כמו:
- רישום (logging)
- בקרת גישה
- מדידת זמן
- שמירה במטמון (caching)
- ניטור ומדדים (instrumentation)
נקודות חשובות על דקורטורים:
- ניתן לערום דקורטורים אחד על השני (stacking) – כלומר להחיל כמה דקורטורים על אותה פונקציה, והיא תיעטף בשכבות רבות.
- כדי לשמר את המטא-נתונים של הפונקציה המקורית (כמו שמה או תיעוד ה-docstring שלה), יש להשתמש ב-functools.wraps בתוך ההגדרה של ה-wrapper. לדוגמה, להוסיף @functools.wraps(func) מעל הפונקציה הפנימית wrapper יעביר את המטא-נתונים מ-func אל wrapper.
- דקורטורים יכולים גם לפעול על מחלקות – לשנות או להוסיף התנהגות להגדרה של מחלקה. לדוגמה, @dataclass הוא דקורטור שמוסיף אוטומטית מתודות למחלקה כמו init, repr ועוד.
- מעבר לעטיפה פשוטה של פונקציות, פייתון תומכת גם בדפוסי דקורטורים מתקדמים. אחד הדוגמאות הבולטות הוא @property, שהופך מתודה למאפיין מחושב – ומשתמשים בו לרוב יחד עם @<property>.setter ו-@<property>.deleter.
דקורטורים יכולים לשמש גם כ-מחוללי דקורטורים (decorator factories) – כלומר, דקורטורים שמקבלים פרמטרים בעצמם באמצעות עטיפה נוספת.