Execute Program

Python in Detail: Attribute Built-Ins

Welcome to the Attribute Built-Ins lesson!

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

  • We usually assign instance attributes in the constructor (.__init__), but we can also assign or replace them at any other time.

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

    keanu = Cat("Keanu")
    keanu.vaccinated = False
    keanu.vaccinated
    Result:
    FalsePass Icon
  • In practice, assigning attributes from outside of the class is usually a bad idea. Most of the time, a class should manage its own attributes. If we assign .vaccinated from outside of the class, then that attribute might not exist on some cats. In that case, accessing it raises an exception.

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

    cats = [
    Cat("Keanu"),
    Cat("Ms. Fluff"),
    ]

    cats[1].vaccinated = True
    vaccination_count = sum([cat.vaccinated for cat in cats])
    vaccination_count
    Result:
    AttributeError: 'Cat' object has no attribute 'vaccinated'Pass Icon
  • We shouldn't use attributes in the way shown above, but some code does it anyway. Fortunately, Python gives us some built-ins to work with attributes that may not exist. We can use hasattr(some_obj, some_attr) to decide whether an attribute exists.

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

    keanu = Cat("Keanu")
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    hasattr(keanu, "name")
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    hasattr(keanu, "vaccinated")
    Result:
    FalsePass Icon
  • hasattr doesn't care about the attribute's value. It returns True even when the value is None.

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

    empty_cat = Cat(None)
    hasattr(empty_cat, "name")
    Result:
    TruePass Icon
  • We can use hasattr to check for attributes before accessing them. The next example counts cats that don't have the .vaccinated attribute.

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

    cats = [
    Cat("Keanu"),
    Cat("Wilford"),
    Cat("Ms. Fluff"),
    ]

    cats[1].vaccinated = True
    missing_vaccinations = sum([not hasattr(cat, "vaccinated") for cat in cats])
    missing_vaccinations
    Result:
    2Pass Icon
  • getattr(some_obj, some_attr) is an alternate way to look up object attributes. It returns the value of the attribute if it exists, or raises an AttributeError if it doesn't exist.

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

    keanu = Cat("Keanu")
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    getattr(keanu, "vaccinated")
    Result:
    AttributeError: 'Cat' object has no attribute 'vaccinated'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu.vaccinated = True
    getattr(keanu, "vaccinated")
    Result:
    TruePass Icon
  • If we want to avoid raising an exception, we can combine hasattr with getattr to return a default value when the attribute doesn't exist.

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

    keanu = Cat("Keanu")
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    def get_attr_if_exists(obj, attr, default):
    if hasattr(obj, attr):
    return getattr(obj, attr)
    else:
    return default
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    get_attr_if_exists(keanu, "name", None)
    Result:
    'Keanu'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    get_attr_if_exists(keanu, "vaccinated", None)
    Result:
    NonePass Icon
  • That function works, but we don't have to write it ourselves! getattr takes an optional default value as its third argument.

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

    keanu = Cat("Keanu")
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    getattr(keanu, "vaccinated", False)
    Result:
    FalsePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    getattr(keanu, "name", "(unknown name)")
    Result:
    'Keanu'Pass Icon
  • If there's a getattr, there must be a setattr. It works as expected: setattr(some_obj, some_attr, some_value) does the same thing as some_obj.name = some_value.

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

    keanu = Cat("Keanu")
    setattr(keanu, "vaccinated", True)
    keanu.vaccinated
    Result:
    TruePass Icon
  • When we know the name of the attribute, it's better to use a regular assignment statement, like some_obj.name = value. But setattr is useful when we don't know the attribute name in advance. For example, sometimes we want code that works with any attribute, like in the code problem below. Without setattr or something similar to it, this code problem would be unsolvable.

  • Here's a code problem:

    Write a function, setattr_default. It takes three arguments:

    • An object.
    • An attribute name.
    • A default value.

    If the object already has an attribute with that name, setattr_default does nothing: it never overwrites attributes. But if the object doesn't have the attribute, setattr_default sets it to the default value. This is similar to the dict.setdefault method that we saw in an earlier lesson.

    def setattr_default(obj, attr, default):
    if not hasattr(obj, attr):
    setattr(obj, attr, default)
    class Cat:
    def __init__(self, attr):
    self.attr = attr

    ms_fluff = Cat("Ms. Fluff")
    setattr_default(ms_fluff, "vaccinated", False)
    assert ms_fluff.vaccinated == False

    setattr_default(ms_fluff, "vaccinated", True)
    assert ms_fluff.vaccinated == False
    Goal:
    None
    Yours:
    NonePass Icon
  • Finally, we can completely remove an attribute with delattr. The attribute is completely deleted, so trying to access it raises an AttributeError.

  • >
    class Cat:
    def __init__(self, name, age):
    self.name = name
    self.age = age
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu", 2)
    keanu.age
    Result:
    2Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu", 2)
    delattr(keanu, "age")
    hasattr(keanu, "age")
    Result:
    FalsePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu", 2)
    delattr(keanu, "age")
    keanu.age
    Result:
    AttributeError: 'Cat' object has no attribute 'age'Pass Icon
  • Trying to delete an attribute that doesn't exist is also an error.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu", 2)
    delattr(keanu, "favorite_food")
    Result:
    AttributeError: 'Cat' object has no attribute 'favorite_food'Pass Icon