מעטרים בפייתון
מאמר זה מסביר על מעטרים בפייתון.
YouTube Video
מעטרים בפייתון
מעטרים בפייתון הם פיצ'ר חזק שנועד להוסיף פונקציונליות נוספת לפונקציות או שיטות. מעטרים מאפשרים להוסיף פונקציונליות חדשה מבלי לשנות את הקוד הקיים, וכך לשפר את השימוש החוזר והתחזוקה של הקוד.
יסודות המעטרים
מעטרים בפייתון פועלים באמצעות קבלת פונקציה כארגומנט והחזרת פונקציה חדשה עם פונקציונליות נוספת. באמצעות מעטרים ניתן להוסיף בקלות עיבוד מוקדם או עיבוד מאוחר לפונקציות.
ללא מעטרים, שינוי ההתנהגות של פונקציה מחייב עריכה ישירה של הפונקציה עצמה. עם מעטרים, ניתן להרחיב פונקציונליות מבלי לשנות את המימוש המקורי.
מבנה בסיסי של מעטר
1def my_decorator(func):
2 def wrapper():
3 print("Before the function call")
4 func()
5 print("After the function call")
6 return wrapper
7
8@my_decorator
9def say_hello():
10 print("Hello!")
11
12say_hello()
בדוגמה זו, הפונקציה my_decorator
משמשת כמעטר, ועוטפת את הפונקציה say_hello
. מעטר מוחל באמצעות התחביר @my_decorator
, וכאשר הפונקציה say_hello()
נקראת, העיבוד המוקדם והמאוחר של המעטר מתבצע באופן אוטומטי.
כיצד מעטרים פועלים
מעטרים עובדים בשלבים הבאים.
-
המעטר מקבל פונקציה (או שיטה) כארגומנט.
-
המעטר מגדיר פונקציית עטיפה לביצוע הפונקציה המקורית.
-
פונקציית העטיפה מבצעת עיבוד נוסף לפני או אחרי ביצוע הפונקציה המקורית.
-
המעטר מחזיר את פונקציית העטיפה. כתוצאה מכך, הפונקציה המקורית מוחלפת בפונקציה חדשה ומעוטרת.
מעטרים לפונקציות עם ארגומנטים
כאשר מחילים מעטר על פונקציה עם ארגומנטים, יש להתאים את פונקציית העטיפה כך שתקלוט אותם.
1def my_decorator(func):
2 def wrapper(*args, **kwargs):
3 print("Before the function call")
4 result = func(*args, **kwargs)
5 print("After the function call")
6 return result
7 return wrapper
8
9@my_decorator
10def greet(name):
11 print(f"Hello, {name}!")
12
13greet("Alice")
בעזרת שימוש ב- *args
ו- **kwargs
, ניתן לטפל בפונקציות שמקבלות כל מספר של ארגומנטים וארגומנטים במילות מפתח. דבר זה מאפשר להחיל מעטרים באופן כללי על פונקציות עם כל סוג של ארגומנטים.
דוגמאות ליישום דקורטורים
דקורטור עבור תיעוד (לוגינג)
דקורטורים משמשים לעיתים קרובות להוספת יכולות תיעוד (לוגינג). לדוגמה, על ידי יצירת דקורטור שמבצע תיעוד (לוג) לפני ואחרי הרצת פונקציה, ניתן לתעד מתי הפונקציה נקראה וכמה זמן לוקח לה לפעול.
1import time
2
3def log_time(func):
4 def wrapper(*args, **kwargs):
5 start_time = time.time()
6 print(f"Calling function '{func.__name__}'...")
7 result = func(*args, **kwargs)
8 end_time = time.time()
9 print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10 return result
11 return wrapper
12
13@log_time
14def long_task(duration):
15 time.sleep(duration)
16 print("Task completed!")
17
18long_task(2)
בדוגמה זו, דקורטור בשם log_time
מודד את זמן הביצוע של פונקציה ומדפיס אותו בתור לוג. הפונקציה long_task
עטופה על ידי הדקורטור וזמן הביצוע שלה מתועד במהלך הריצה.
דקורטור לניהול הרשאות
ניתן להשתמש בדקורטורים גם לניהול הרשאות. לדוגמה, ניתן להגביל תהליכים באמצעות בדיקה אם למשתמש יש הרשאות מסוימות.
1def requires_permission(user_role):
2 def decorator(func):
3 def wrapper(*args, **kwargs):
4 if user_role != "admin":
5 print("Permission denied!")
6 return
7 return func(*args, **kwargs)
8 return wrapper
9 return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13 print(f"User {user_id} has been deleted.")
14
15delete_user(123) # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456) # Permission denied!
הדקורטור requires_permission
מגביל את הרצת הפונקציה בהתאם לתפקידו של המשתמש. על ידי ריכוז לוגיקה כזו בתוך דקורטור, ניתן למנוע פיזור של לוגיקת ניהול הרשאות ברחבי הקוד ולהגביר את קריאותו.
שימוש מקונן בדקורטורים
ניתן ליישם מספר דקורטורים. כאשר מספר דקורטורים מוחלים על פונקציה אחת, הם פועלים לפי הסדר מלמעלה למטה.
1def uppercase(func):
2 def wrapper(*args, **kwargs):
3 result = func(*args, **kwargs)
4 return result.upper()
5 return wrapper
6
7def exclaim(func):
8 def wrapper(*args, **kwargs):
9 result = func(*args, **kwargs)
10 return result + "!"
11 return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16 return f"Hello, {name}"
17
18print(greet("Alice")) # "HELLO, ALICE!"
בדוגמה זו, שני דקורטורים מוחלים על הפונקציה greet
. הדקורטור @exclaim
מוסיף סימן קריאה בסוף המחרוזת, ולאחר מכן הדקורטור @uppercase
הופך את המחרוזת לאותיות גדולות.
דקורטורים עבור פונקציות מחלקה
ניתן להשתמש בדקורטורים גם עם פונקציות מחלקה (class methods). זה שימושי במיוחד כאשר רוצים לשלוט בהתנהגות הפונקציות בתוך מחלקה. כאשר מיישמים דקורטורים על פונקציות מחלקה, יש לתת תשומת לב לארגומנטים כמו self
או cls
.
1def log_method_call(func):
2 def wrapper(self, *args, **kwargs):
3 print(f"Calling method '{func.__name__}'...")
4 return func(self, *args, **kwargs)
5 return wrapper
6
7class MyClass:
8 @log_method_call
9 def greet(self, name):
10 print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")
בדוגמה זו, הדקורטור log_method_call
מוחל על הפונקציה greet
כדי לבצע לוג כאשר הפונקציה נקראת.
הזהרות לגבי שימוש בדקורטורים
ישנם מספר שיקולים שיש לקחת בחשבון בעת שימוש בדקורטורים. דקורטורים עלולים לשנות את שם הפונקציה המקורית ואת מחרוזת התיעוד שלה (כגון __name__
או __doc__
), ולכן מומלץ להשתמש ב-functools.wraps
כדי לשמר מידע זה.
1import functools
2
3def my_decorator(func):
4 @functools.wraps(func)
5 def wrapper(*args, **kwargs):
6 print("Before the function call")
7 return func(*args, **kwargs)
8 return wrapper
השימוש ב-@functools.wraps
מבטיח כי המידע המטאדאטה של הפונקציה המקורית יעבור כראוי לפונקציית העטיפה.
סיכום
מעטרים (Decorators) בפייתון הם כלי חזק מאוד שמאפשר להוסיף פונקציונליות נוספת לפונקציות ולמתודות בצורה תמציתית. ניתן להשתמש בהם בתרחישים שונים כדי להפחית כפילות בקוד ולשפר את תחזוקתיותו ואת יכולת השימוש החוזר בו. על ידי הבנת אופן הפעולה של מעטרים, ניתן לכתוב קוד יעיל וגמיש יותר.
תוכלו לעקוב אחר המאמר שלמעלה באמצעות Visual Studio Code בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.