לדלג לתוכן

7.2 מחלקה (לא חובה) הרצאה

הקדמה

  • בפרק נלמד כל מיני דברים מיוחדים בפייתון שמתקשרים למחלקות, הפרק הוא אופציונאלי (לא חובה)כי הוא מדבר על פיצ'רים מתקדמים בפייתון, אך מומלץ!

ממברים של מחלקה

  • השדה __dict__ מעניק לנו גישה למילון של כל הממברים של המחלקה
    class MyClass:  
        def __init__(self):  
            self.x = 5  
    
        def member(self, param):  
            return self.x + param
    
    MyClass.__dict__
    
  • אנחנו יכולים לגשת לממברים שונים באמצעות המילון הזה,
    MyClass.__dict__['member']
    
  • אנחנו יכולים באמצעתו לגשת למתודות שונות במחלקה
    obj = MyClass()
    method = MyClass.__dict__['member']
    
    method(obj, 4)
    method(obj, 3)
    method(obj, 1)
    
  • לצערנו כל פעם שנקרא למתודה במחלקה אנחנו נצטרך להעביר לה את האובייקט כהפרמטר self שלה, וזה לא נוח לנו.
  • בשביל לפתור את הבעיה נשתמש בפונקציה __get__ שמאפשרת לנו לקבל את המתודה לאחר שהביאו לה את self בצורה הבאה:
    obj = MyClass() 
    method = MyClass.__dict__['member'].__get__(obj, MyClass)
    
    method(4)
    method(3)
    method(1)
    
  • באמצעות שילוב של __dict__ ושל __get__ אנחנו יכולים לגשת למתודות של המחלקה!
  • למעשה ככה פייתון עובד, כאשר אנחנו כותבים את הביטויי הבא:
    obj.member(3)
    
  • פייתון מתרגם אותו ל -
    MyClass.__dict__['member'].__get__(obj, MyClass)(3)
    
  • אבל איך פייתון ניגש למתודות סטטיות? הריי מתודות סטטיות לא מקבלות את self כמו שאנחנו העברנו לו,
  • זה יכול לקרות באחת משלושת הדרכים הבאות:
    MyClass.__dict__['member']
    MyClass.__dict__['member'].__get__(None, MyClass)
    MyClass.__dict__['member'].__get__(obj, MyClass).__func__
    
  • כל המתודות האלו זהות לביטוי הבא:
    MyClass.member
    
  • אז לסיכום, הממברים __dict__, __get__, ו - __func__ כולם נקראים כאשר פייתון מנסה לגשת לממברים סטטית או דינמית.

נצור מחדש את הדקורטור staticmethod

  • בהתאם לידע החדש שרכשנו, נשתמש בclass כדי לצור את הדקורטור staticmethod בעצמנו
  • איך נעשה את זה?
    • דקורטור מקבל פונקציה כפרמטר, אז ניישם את __init__ מחדש כדי לקבל את הפנקציה כפרמטר
    • כאשר פייתון יקרא ל__get__ על הדקורטור שלנו אנחנו נצטרך להביא לו את הפונקציה המקורית ללא קשר לאיזה אובייקט הוא העביר לנו.
    • לפעמים כשמנסים לגשת לפונקציות סטטית יכולים לגשת אלייה באמצעות __func__, אז נוודא שגם במקרה שהוא מנסה לגשת לשדה זה נחזיר לו את הפונקציה המקורית.
      class my_staticmethod:
          def __init__(self, callable):
              self.f = callable
          def __get__(self, obj=None, type):
              return self.f
          @property
          def __func__(self):
              return self.f
      
      class MyClass
          @my_staticmethod
          def member(a):
              return a+1
      
      MyClass.__dict__['member'].__get__(None, MyClass)
      MyClass.__dict__['member']
      
      obj = MyClass()
      MyClass.__dict__['member'].__get__(obj, MyClass)
      MyClass.__dict__['member'].__get__(obj, MyClass).__func__
      
      MyClass.member(3)
      
  • יצרנו בעצמנו את הדקורטור staticmethod, כל הכבוד לנו! שימו לב שבגלל שפייתון ממש בעצמו את הדקורטור staticmethod לא באמת בשפת פייתון, הדקורטור שכתבנו לא יתנהג בדיוק כמו staticmethod אז אל תשתמשו בו בקוד אמיתי :)

איך אובייקט נולד?

  • אז איך באמת אובייקט נולד?
    obj = MyClass()
    
  • ברגע שאנחנו מריצים את השורה למעלה הדבר הראשון שנקרא זה המתודה __new__, ומטרתה היא לצור אובייקט חדש מסוג המחלקה MyClass, המתודה הזו מוגדרת על ידי המחלקה object, ואנחנו לא מיישמים אותה מחדש (כמו שאנחנו עושים לרוב), כי זה עלול לשבור את הדרך שבה אובייקטים נוצרים.
  • אחרי ש__new__ יוצר לפייתון אובייקט חדש כזה, נקראת המתודה __init__ של המחלקה שאנחנו כבר מכירים. מטרת המחלקה היא לאתחל שדות שונים לאובייקט החדש, ובדרך כלל אנחנו ניישם מחדש את המתודה הזו.
  • אפשר לדמיין את הקוד שאחרי על יצירת אובייקטים בפייתון לכזה:
    obj = cls.__new__(cls)
    if isinstance(obj, cls):
        cls.__init__(obj)