שלום אורח התחבר

חיפוש באוביקט מקונן בפייתון

הבלוג מתעדכן פעמיים בשבוע בימי שני וחמישי. הזינו כתובת מייל וקבלו בחינם את הפוסטים החדשים לתיבה.

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

תוכן עניינים

  1. הקוד
  2. בעיות במימוש

1הקוד

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

movie_data = {
  "movies": {
    "Spaceballs": {
      "imdb_stars": 7.1,
      "rating": "PG",
      "length": 96,
      "director": "Mel Brooks",
    },
    "Robin_Hood_Men_in_Tights": {
      "imdb_stars": 6.7,
      "rating": "PG-13",
      "length": 104,
      "director": "Mel Brooks",
    }
  }
}

movie_box = Box(movie_data)
print(movie_box.movies.Robin_Hood_Men_in_Tights.imdb_stars)

הפונקציה __getattr__ נקראת פעמיים: קודם על האוביקט moviebox עם השם `RobinHoodMeninTightsולאחר מכן על האוביקט שחזר מהקריאה הקודמת עם השםimdbstars`. לכן, הטריק במימוש הוא פשוט להחזיר אוביקט Box חדש בכל קריאה:

class Box:
    def __init__(self, d):
        self._d = d

    def __getattr__(self, name):
        if type(self._d[name]) == dict:
            return Box(self._d[name])
        else:
            return self._d[name]

כך אפשר לשלוף שדות מהאוביקט בכל עומק שתרצו.

2בעיות במימוש

הקוד הבא מדפיס תוצאה מפתיעה:


movie_data = {
  "_d": 7,
  "movies": {
    "Spaceballs": {
      "imdb_stars": 7.1,
      "rating": "PG",
      "length": 96,
      "director": "Mel Brooks",
    },
    "Robin_Hood_Men_in_Tights": {
      "imdb_stars": 6.7,
      "rating": "PG-13",
      "length": 104,
      "director": "Mel Brooks",
    }
  }
}

movie_box = Box(movie_data)
print(movie_box._d)

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

שינוי קטן במחלקה Box מאפשר לתקן גם את זה:

class Box:
    def __init__(self, d):
        self._d = d

    def __getattribute__(self, name):
        _d = object.__getattribute__(self, '_d')
        val = _d[name]
        if type(val) == dict:
            return Box(val)
        else:
            return _d[name]

הפונקציה __getattribute__ נקראת בכל פעם שמנסים לשלוף מאפיין, גם אם מאפיין זה כבר מוגדר באוביקט. בפונקציה זו עלינו להשתמש ב object.__getattribute__ כדי לקבל את המאפיין _d בלי ליפול לרקורסיה אינסופית, ואז אפשר לשלוף תמיד את הערך המתאים מהמילון.


נהניתם מהפוסט? מוזמנים לשתף ולהגיב