דקורטורים למחלקות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Python של Coddy. שיעור 40 מתוך 64.
מעטרי מחלקה מאפשרים לך לשנות או לשפר מחלקות באמצעות עטיפתן בפונקציה אחרת. הם פועלים כמו מעטרי פונקציות, אך מוחלים על מחלקות שלמות.
הנה מחלקה פשוטה ללא קישוט:
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"צרו דקורטור למחלקה שמוסיף מתודה חדשה:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # הוסף את המתודה למחלקה
return clsהחל את הדקורטור על מחלקה באמצעות @:
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"כעת למחלקה יש גם את השיטות המקוריות וגם את השיטות שנוספו:
person = EnhancedPerson("Alice")
print(person.greet()) # שלום, שמי אליס
print(person.farewell()) # להתראות מאליסאפשר גם לעטוף מתודה קיימת: לשמור את המקורית כדי שעדיין יהיה אפשר לקרוא לה מתוך העוטפת. זה שימושי כשרוצים להוסיף התנהגות (כמו מעקב או רישום ביומן) סביב מתודה שכבר קיימת במחלקה:
def add_tracking(cls):
original_greet = cls.greet # שמור את המתודה המקורית
def tracked_greet(self):
print(f"greet was called") # התנהגות נוספת לפני הקריאה
return original_greet(self) # קרא למתודה המקורית
cls.greet = tracked_greet # החלף את המתודה במחלקה
return cls
@add_tracking
class TrackedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"כשקוראים עכשיו ל־greet, העוטפת רצה תחילה ואז מעבירה את הביצוע למקור:
person = TrackedPerson("Alice")
print(person.greet())
# הפונקציה greet נקראה
# שלום, שמי Aliceהשלבים העיקריים לעטיפת מתודה קיימת הם:
1. שומרים את המתודה המקורית: original = cls.method
2. מגדירים פונקציה חדשה שקוראת ל־original(self) בתוספת כל לוגיקה נוספת.
3. משייכים את הפונקציה החדשה בחזרה למחלקה: cls.method = new_function
4. מחזירים את המחלקה ששונתה.
נקודה מרכזית: מעטרי מחלקות מקבלים מחלקה כקלט, משנים או משפרים אותה ומחזירים את המחלקה ששונתה. הם יכולים להוסיף מתודות, לשנות מאפיינים או לעטוף פונקציונליות קיימת. השתמשו ב־@decorator_name מעל הגדרת המחלקה כדי להחיל אותם. כך אפשר להרחיב את הפונקציונליות של מחלקה בדרך נקייה, ללא ירושה.
אתגר
בינוניבאתגר הזה תממשו דקורטור למחלקה שמוסיף יכולת מעקב אחר קריאות למתודות.
decorator.py- מימוש הדקורטור למחלקהadd_countercalculator.py- מכיל את המחלקות שישתמשו בדקורטור שלכם
הקובץ driver.py מכיל תרחישי בדיקה מקיפים שיאמתו את המימוש שלכם מול מגוון מקרי שימוש, דפוסי ירושה ומקרי קצה. אין צורך לשנות את הקובץ הזה, אך העיון בו יעזור לכם להבין את ההתנהגות המצופה.
נסו בעצמכם
from calculator import Calculator
# מטפל מקיף במקרי בדיקה
test_case = input()
if test_case == "basic_functionality":
calc = Calculator()
print(calc.add(5, 3)) # אמור להדפיס 8
print(calc.add(2, 7)) # אמור להדפיס 9
print(calc.subtract(10, 4)) # אמור להדפיס 6
print(calc.call_counts) # אמור להדפיס {'add': 2, 'subtract': 1}
elif test_case == "multiple_instances":
calc1 = Calculator()
calc2 = Calculator()
print(calc1.add(5, 3)) # אמור להדפיס 8
print(calc2.add(2, 7)) # אמור להדפיס 9
print(calc1.subtract(10, 4)) # אמור להדפיס 6
# שני המופעים אמורים לחלוק את אותו call_counts
print(calc1.call_counts) # אמור להדפיס {'add': 2, 'subtract': 1}
print(calc2.call_counts) # אמור להדפיס {'add': 2, 'subtract': 1}
print(calc1.call_counts is calc2.call_counts) # אמור להדפיס True
elif test_case == "method_call_order":
calc = Calculator()
print(calc.add(1, 2)) # אמור להדפיס 3
print(calc.subtract(5, 2)) # אמור להדפיס 3
print(calc.add(10, 20)) # אמור להדפיס 30
print(calc.add(7, 3)) # אמור להדפיס 10
print(calc.call_counts) # אמור להדפיס {'add': 3, 'subtract': 1}
elif test_case == "large_values":
calc = Calculator()
print(calc.add(1000000, 2000000)) # אמור להדפיס 3000000
print(calc.subtract(5000000, 2000000)) # אמור להדפיס 3000000
print(calc.call_counts) # אמור להדפיס {'add': 1, 'subtract': 1}
elif test_case == "negative_values":
calc = Calculator()
print(calc.add(-5, -3)) # אמור להדפיס -8
print(calc.subtract(-10, -4)) # אמור להדפיס -6
print(calc.call_counts) # אמור להדפיס {'add': 1, 'subtract': 1}
elif test_case == "float_values":
calc = Calculator()
print(calc.add(5.5, 3.2)) # אמור להדפיס 8.7
print(calc.subtract(10.5, 4.2)) # אמור להדפיס 6.3
print(calc.call_counts) # אמור להדפיס {'add': 1, 'subtract': 1}
elif test_case == "zero_values":
calc = Calculator()
print(calc.add(0, 0)) # אמור להדפיס 0
print(calc.subtract(0, 0)) # אמור להדפיס 0
print(calc.call_counts) # אמור להדפיס {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # אמור להדפיס 8
print(calc.call_counts) # אמור להדפיס {'add': 1, 'subtract': 0}
# אפס את המונה
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # אמור להדפיס 9
print(calc.call_counts) # אמור להדפיס {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # אמור להדפיס 8
print(calc.call_counts) # אמור להדפיס {'add': 1, 'subtract': 0}
# שנה את המונה ישירות
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # אמור להדפיס 9
print(calc.call_counts) # אמור להדפיס {'add': 101, 'subtract': 0}
elif test_case == "performance_test":
calc = Calculator()
# בצע 1000 פעולות add
for i in range(1000):
calc.add(i, i+1)
# בצע 500 פעולות subtract
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # אמור להדפיס {'add': 1000, 'subtract': 500}השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםהפרמטר selfמתודותמאפייניםשיטת הבנאי (__init__)חזרה: מחשבון פשוט5פולימורפיזם
בחינה מחדש של דריסת מתודותטיפוסיות ברווזמחלקות ומתודות אבסטרקטיותעיצוב ממשקיםסיכום – מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה לעומת הורשהMixinsמתודות סטטיות ומתודות מחלקהדקורטורים למחלקותמנהלי הקשרתרגלו בעצמכם: קומפיילר Python אונליין