Execute Program

Python in Detail: __dict__

Welcome to the __dict__ lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • Python is flexible when working with object attributes. We can access attributes with some_obj.some_attr, or via the hasattr and getattr built-in functions. We can also set attributes directly on the instance with some_obj.some_attr = value, or with setattr.

  • This flexibility is powered by dictionaries, which may be a surprise at first! For regular classes like the ones that we've defined so far, attributes are stored in some_obj.__dict__. The dictionary's keys are the attribute names, and its values are the attribute values. Functions like setattr manipulate that dictionary, but we can also manipulate it directly when needed.

  • >
    class User:
    def __init__(self, id, admin):
    self.id = id
    self.admin = admin

    amir = User(1, False)
    amir.__dict__
    Result:
  • >
    class Cat:
    def __init__(self, name):
    self.name = name

    keanu = Cat("Keanu")
    keanu.__dict__
    Result:
    {'name': 'Keanu'}Pass Icon
  • The .__dict__ dictionary is not merely another way to look at attributes; it's where the attributes are actually stored! Modifying it changes the object's attributes. In the next example, we manipulate .__dict__ to change cat.name.

  • >
    class Cat:
    def __init__(self, name):
    self.name = name

    keanu = Cat("Keanu")
    keanu.__dict__["name"] = "Ms. Fluff"
    keanu.name
    Result:
    'Ms. Fluff'Pass Icon
  • Because .__dict__ is a regular dictionary, all of the usual dictionary operations work. For example, we can list its keys.

  • >
    class Cat:
    def __init__(self, name):
    self.name = name

    keanu = Cat("Keanu")
    keanu.vaccinated = True

    # Note that the instance's two attribute keys will come out in the order
    # they were defined.
    list(keanu.__dict__.keys())
    Result:
    ['name', 'vaccinated']Pass Icon
  • We can also create new attributes by adding new keys, like the vaccinated attribute in the next example.

  • >
    class Cat:
    def __init__(self, name):
    self.name = name

    keanu = Cat("Keanu")
    keanu.__dict__["vaccinated"] = True

    keanu.vaccinated
    Result:
    TruePass Icon
  • The attribute dictionary stores attributes assigned to the instance. It doesn't include methods, which are associated with the class rather than a specific instance.

  • >
    class Orange:
    def calories(self):
    return 75

    orange = Orange()
    "calories" in orange.__dict__
    Result:
    FalsePass Icon
  • Classes themselves are objects, so they also have a .__dict__! That's where the methods live.

  • >
    class Cat:
    family = "felidae"

    def __init__(self, name):
    self.name = name

    def vaccinated(self):
    return False
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    "__init__" in Cat.__dict__
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    "vaccinated" in Cat.__dict__
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    "run" in Cat.__dict__
    Result:
    FalsePass Icon
  • The class dictionary also contains class attributes.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    "name" in Cat.__dict__
    Result:
    FalsePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    "family" in Cat.__dict__
    Result:
    TruePass Icon
  • We can even call methods directly from .__dict__.

  • >
    class Cat:
    def __init__(self, name):
    self.name = name

    def vaccinated(self):
    return False

    keanu = Cat("Keanu")
    vaccinated_method = Cat.__dict__["vaccinated"]
    vaccinated_method(keanu)
    Result:
    FalsePass Icon
  • Most values in Python have a .__dict__, but there are exceptions. One notable exception is dictionaries themselves.

  • >
    a_dict = {}
    hasattr(a_dict, "__dict__")
    Result:
    FalsePass Icon
  • Why don't dictionaries have a .__dict__? There are many reasons, but one simple one is performance. For performance reasons, we can't change built-in types like dicts in the same way that we can modify custom classes like Cat. Integers, floats, strings, lists, and tuples also lack a .__dict__ for the same reason.

  • >
    hasattr([], "__dict__")
    Result:
    FalsePass Icon
  • >
    hasattr(5, "__dict__")
    Result:
    FalsePass Icon
  • Here's a code problem:

    Write a setattr_via_dict function. It tries to set an attribute by modifying the target object's .__dict__. However, not all Python values have a .__dict__.

    If the object has a .__dict__ attribute, set the attribute value and return True. Otherwise return False.

    def setattr_via_dict(obj, attr, value):
    if hasattr(obj, "__dict__"):
    obj.__dict__[attr] = value
    return True
    else:
    return False
    some_dict = {
    "a": 1
    }
    assert setattr_via_dict(some_dict, "some_attr", 2) == False
    assert some_dict["a"] == 1

    class C:
    pass

    c = C()
    assert setattr_via_dict(c, "some_attr", 1)
    assert c.some_attr == 1
    Goal:
    None
    Yours:
    NonePass Icon