יום חמישי, 29 ביוני 2023

ממש קצת על בנית דקורטורים בפייתון.

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


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

בואו נצלול לקצת קוד.
במידה ויש לנו את הפונקציה הבאה שמורידה את דף הבית של ynet, בהנתן שאנו רוצים למדוד ולהפדיס את זמן הריצה שלה (בפועל לרוב נשתמש ב logger אבל נשתמש ב print כדי לעבוד הכי פשוט שאפשר כאן).
הפונקציה בבסיסה נראית ככה: 



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




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




מה קורה כאן? למעשה יש לנו פונקציה בשם timeit שהיא עוטפת את decorator וגם מחזירה אותו. 
כשאנחנו נבצע בקוד קריאה לפוןנקציה המקורית שלנו, כלומר ל fetch_data מה שיקרה זה שפייטון מאחורי הקלעים במקום באמת להריץ את הפונקציה היא תפעיל את timeit עם פרמטר שהוא למעשה הפןנקציה עצמה (כלומר fetch_data)
הפונקציה timeit  תחזיר את הפונקציה decorator שלמעשה ירוץ ובתוכו יקרה ״הקסם״.
הוא יהיה זה שיריץ את הפונקציה שאותה המשתמש בחר להפעיל (כלומר בקונטקסט של הדקורטור,  את f) כששורה לפני ושורה אחרי הוא יהיה אחראי על מדידת זמן הריצה והדפסתו.

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




בשורה 16, השורה של הוספת הדקורטור ל fetch_data,  הוספנו עכשיו גם פרמטר מסוג מחרוזת. כתוצאה מכך,  יש לנו בשביל הפרמטר, למעשה קריאה לפונקציה (כי הוספנו את הסוגריים) ולא סתם העברה של הפונקציה. 
מה שיקרא בפועל זה שעכשיו פייטון ממש יריץ את הפונקציה, ממש יבצע לה אבולואציה,  ולכן, שימו לב עטפנו את מה שהיה הדקורטור המקורי בעוד פונקציה, כתוצאה מכך, הפונקציה timeit עכשיו תפקידה הוא להחזיר את כל הדקורטור.  בשיטה הזאת אנחנו מקבלים את הפרמטר (name) למעטפת החיצונית, ואולי כל מה שהיה הדקורטור המקורי (שפתים הפןנקציות הפנימיות) הן המימוש של הדקורטור עצמו. 
שימו לב: שמות הפונקציות הם שמות שמקובלים בבניית דקורטורים, אבל אפשר לתת כל שם שרוצים, קצת כמו ש self הוא המקובל כפרמטר הראשון בפונקציות של מחלקה למרות שאפשר להשתמש תיאורטית ב this או ב s או מה שרק רוצים (אך אינו מומלץ). 


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



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

אין תגובות:

הוסף רשומת תגובה