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 thehasattrandgetattrbuilt-in functions. We can also set attributes directly on the instance withsome_obj.some_attr = value, or withsetattr.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 likesetattrmanipulate that dictionary, but we can also manipulate it directly when needed.>
class User:def __init__(self, id, admin):self.id = idself.admin = adminamir = User(1, False)amir.__dict__Result:
>
class Cat:def __init__(self, name):self.name = namekeanu = Cat("Keanu")keanu.__dict__Result:
{'name': 'Keanu'}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 changecat.name.>
class Cat:def __init__(self, name):self.name = namekeanu = Cat("Keanu")keanu.__dict__["name"] = "Ms. Fluff"keanu.nameResult:
'Ms. Fluff'
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 = namekeanu = 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']
We can also create new attributes by adding new keys, like the
vaccinatedattribute in the next example.>
class Cat:def __init__(self, name):self.name = namekeanu = Cat("Keanu")keanu.__dict__["vaccinated"] = Truekeanu.vaccinatedResult:
True
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 75orange = Orange()"calories" in orange.__dict__Result:
False
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 = namedef vaccinated(self):return False- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
"__init__" in Cat.__dict__Result:
True
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
"vaccinated" in Cat.__dict__Result:
True
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
"run" in Cat.__dict__Result:
False
The class dictionary also contains class attributes.
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
"name" in Cat.__dict__Result:
False
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
"family" in Cat.__dict__Result:
True
We can even call methods directly from
.__dict__.>
class Cat:def __init__(self, name):self.name = namedef vaccinated(self):return Falsekeanu = Cat("Keanu")vaccinated_method = Cat.__dict__["vaccinated"]vaccinated_method(keanu)Result:
False
Most values in Python have a
.__dict__, but there are exceptions. One notable exception is dictionaries themselves.>
a_dict = {}hasattr(a_dict, "__dict__")Result:
False
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 likeCat. Integers, floats, strings, lists, and tuples also lack a.__dict__for the same reason.>
hasattr([], "__dict__")Result:
False
>
hasattr(5, "__dict__")Result:
False
Here's a code problem:
Write a
setattr_via_dictfunction. 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 returnTrue. Otherwise returnFalse.def setattr_via_dict(obj, attr, value):if hasattr(obj, "__dict__"):obj.__dict__[attr] = valuereturn Trueelse:return Falsesome_dict = {"a": 1}assert setattr_via_dict(some_dict, "some_attr", 2) == Falseassert some_dict["a"] == 1class C:passc = C()assert setattr_via_dict(c, "some_attr", 1)assert c.some_attr == 1- Goal:
None
- Yours:
None