Execute Program

Python in Detail: Class Attributes

Welcome to the Class Attributes lesson!

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

  • We've seen attributes on instances, but classes can also have attributes.

  • >
    class Cat:
    favorite_food = "salmon"

    def __init__(self, name):
    self.name = name
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    Cat.favorite_food
    Result:
    'salmon'Pass Icon
  • Class attributes are accessible via Cat.favorite_food, but they're also accessible via some_cat.favorite_food. When we access an attribute, Python checks for an instance attribute first, and only then checks for a class attribute.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu")
    keanu.favorite_food
    Result:
    'salmon'Pass Icon
  • If we change the class attribute's value, the change shows up on all of the instances.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu")
    Cat.favorite_food = 'chicken'
    keanu.favorite_food
    Result:
    'chicken'Pass Icon
  • If we define Cat.favorite_food but also define keanu.favorite_food, those are separate attributes. The instance keanu has an attribute, and the class has an attribute with the same name. Python checks for instance attributes before checking for class attributes, so the instance attribute "wins".

  • In the next example, Keanu's favorite food is tuna. But all other instances of Cat still like salmon.

  • >
    class Cat:
    favorite_food = "salmon"

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

    keanu = Cat("Keanu")
    keanu.favorite_food = "tuna"
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu.favorite_food
    Result:
    'tuna'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    Cat.favorite_food
    Result:
    'salmon'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    ms_fluff = Cat("Ms. Fluff")
    ms_fluff.favorite_food
    Result:
    'salmon'Pass Icon
  • We can define and manipulate class attributes like this because Python classes themselves are objects! Keanu is an instance of Cat:

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

    keanu = Cat("Keanu")
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    isinstance(keanu, Cat)
    Result:
    TruePass Icon
  • But the Cat class is an instance of object, like all classes are:

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    isinstance(Cat, object)
    Result:
    TruePass Icon
  • When we assign class attributes, we're setting an attribute on an object. In this case, the object just happens to be a class.

  • All objects are instances of some class. And we just saw that classes are objects. So are classes themselves instances of some class? Yes, but that's a topic for another lesson!

  • The next code problem requires a new function, assert_raises, which we'll use in several places throughout this course. This function is specific to Execute Program, and isn't a part of Python itself. It takes two arguments: an expected exception type and a function.

  • assert_raises calls the function. If the function raises the expected exception, nothing happens. But if the function doesn't raise the expected exception, then assert_raises raises its own exception. The code example below uses assert_raises to check that your code raises a ValueError.

  • Here's a code problem:

    We want to make sure that all cats have unique names. To solve that, we can track all existing cats in a class attribute on the Cat class. When instantiating a new cat, raise a ValueError if another cat already has that name.

    class Cat:
    # Our solution stores the cat objects in this list. It's also OK to store
    # only the names in the list.
    population = []

    def __init__(self, name):
    self.name = name
    for existing_cat in Cat.population:
    if existing_cat.name == self.name:
    raise ValueError("Name conflict")

    Cat.population.append(self)
    ms_fluff = Cat("Ms. Fluff")
    assert_raises(ValueError, lambda: Cat("Ms. Fluff"))

    keanu = Cat("Keanu")
    assert_raises(ValueError, lambda: Cat("Keanu"))
    Goal:
    None
    Yours:
    NonePass Icon