Execute Program

Python for Programmers: Class Basics

Welcome to the Class Basics lesson!

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

  • Python lets us define classes, which combine functions and data to create custom data types.

  • In the next example, we create an empty User class. Python's indentation-based syntax requires us to put something inside of the class, so we use pass. pass is a placeholder keyword intended for exactly this type of situation.

  • >
    class User:
    pass
  • Once we have a User class, we can "instantiate" it, which gives us an object representing an individual user. In Python, instantiation looks like a function call: User().

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    amir = User()
  • By convention, Python class names are always UpperCamelCased. Function and variable names are lower_snake_cased. UserDatabase is a class, but user_database is a function or variable.

  • Our User class is empty, but we can still use it. Python lets us set and get attributes on our instance. This is similar to what we've seen with setting dictionary keys, except we use the . operator rather than [].

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    amir.name = "Amir"
    amir.name
    Result:
    'Amir'Pass Icon
  • Each instance of a class is distinct and tracks its own attributes.

  • >
    class User:
    pass

    amir = User()
    amir.name = "Amir"

    betty = User()
    betty.name = "Betty"
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    amir.name
    Result:
    'Amir'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    betty.name
    Result:
    'Betty'Pass Icon
  • Classes can have methods, which are a kind of function. The most important method is .__init__, called the "constructor" or "initializer".

  • (Technically, constructors and initializers are two different things, but the difference isn't important for day-to-day programming. In Python, both terms are often used for .__init__.)

  • Python automatically calls .__init__ for us when we instantiate a class.

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

    amir = User("Amir")
    amir.name
    Result:
    'Amir'Pass Icon
  • All methods, including .__init__, get the instance as their first argument. We name that argument self. It lets us interact with the instance from inside the method, like the self.name = name line above.

  • Unlike many programming languages, Python has no built-in this or self keyword. The self argument above is just a regular function argument!

  • To prove that self isn't special, the next code example gives it a different name. This is strongly discouraged in the Python community because it's so confusing, but it does work!

  • >
    class User:
    def __init__(do_not_actually_do_this, name):
    do_not_actually_do_this.name = name

    amir = User("Amir")
    amir.name
    Result:
    'Amir'Pass Icon
  • To recap, when we instantiate a user with User("Amir"):

    • Python creates a new instance of User, called self.
    • Python calls the initializer via self.__init__("Amir").
    • Python returns self to us.
  • We can define additional instance methods on our class. Each one takes self as an explicit argument. But when we call a method, like keanu.name_length() below, we don't have to pass self in. Python automatically passes the self argument for us.

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

    def name_length(self):
    return len(self.name)

    keanu = Cat("Keanu")
    keanu.name_length()
    Result:
    5Pass Icon
  • Some programming languages let us refer to attributes directly, for example with len(name) instead of len(self.name). In Python, the self. is mandatory. Directly referencing name instead of self.name raises a NameError, since there's no name variable in scope.

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

    def name_length(self):
    return len(name)

    keanu = Cat("Keanu")
    keanu.name_length()
    Result:
    NameError: name 'name' is not definedPass Icon
  • We also have to explicitly assign attributes in the initializer, like self.name = name above. If the initializer takes an argument, but doesn't use it, then it has no effect.

  • In the next example, the initializer takes age as an argument, but it never assigns it to self.age. When the .is_kitten method accesses self.age, it raises an AttributeError because that attribute doesn't exist.

  • >
    class Cat:
    def __init__(self, name, age):
    # Note that we do nothing with `age`.
    self.name = name

    def is_kitten(self):
    return self.age <= 2

    keanu = Cat("Keanu", 2)
    keanu.is_kitten()
    Result:
    AttributeError: 'Cat' object has no attribute 'age'Pass Icon
  • Methods, including .__init__, are functions. They can use function features like default argument values.

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

    def is_kitten(self):
    return self.age <= 2

    keanu = Cat("Keanu")
    ms_fluff = Cat("Ms. Fluff", 4)

    [keanu.is_kitten(), ms_fluff.is_kitten()]
    Result:
    [True, False]Pass Icon
  • Most Python classes have at least one method that sets an attribute. At the beginning of this lesson, we saw that we can also set attributes from outside of the class, like some_user.name = "Amir". However, relying on this is often a bad idea.

  • For example, we might write our Cat class slightly differently. Instead of accepting the cat's age as a constructor argument, the class might expect us to set the age attribute manually after instantiating it. But that's dangerous: if we forget to set the age attribute, then any code trying to access that attribute raises an AttributeError.

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

    def is_kitten(self):
    return self.age <= 2
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu")
    keanu.age = 2
    keanu.is_kitten()
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    keanu = Cat("Keanu")
    keanu.is_kitten()
    Result:
    AttributeError: 'Cat' object has no attribute 'age'Pass Icon
  • Instances are mutable, so we can modify existing attributes after the instance is instantiated.

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

    def is_kitten(self):
    return self.age <= 2

    keanu = Cat("Keanu", 2)
    is_kitten_this_year = keanu.is_kitten()
    keanu.age += 1
    is_kitten_next_year = keanu.is_kitten()

    [is_kitten_this_year, is_kitten_next_year]
    Result:
    [True, False]Pass Icon
  • Here's a code problem:

    Define a Cat class whose instances have a name attribute. The constructor should take the cat's name as an argument.

    Add a .name_ends_in_y method that returns True when the cat's name ends in "y". To check for whether a string ends in y, call some_string.endswith("y").

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

    def name_ends_in_y(self):
    return self.name.endswith("y")
    assert Cat("Keanu").name_ends_in_y() == False
    assert Cat("Grumpy").name_ends_in_y() == True
    Goal:
    No errors.
    Yours:
    No errors.Pass Icon
  • In an earlier lesson, we used isinstance to check a value against a type.

  • >
    greeting = "hello"
    isinstance(greeting, str)
    Result:
    TruePass Icon
  • This works with our own classes as well.

  • >
    class Point:
    def __init__(self, x, y):
    self.x = x
    self.y = y
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    point1 = Point(3, 2)
    isinstance(point1, Point)
    Result:
    TruePass Icon
  • The is operator checks object identity: are these two objects exactly the same, living at the same location in memory? A given instance is itself, and a class also is itself. However:

    • a given instance is not another instance
    • a class is not another class
    • an instance is not its own class.
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    point1 = Point(3, 2)
    point2 = point1
    point2 is point1
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    # We can create another variable referencing a class!
    Point2 = Point
    Point2 is Point
    Result:
    TruePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    point1 = Point(3, 2)
    point2 = Point(3, 2)
    point1 is point2
    Result:
    FalsePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    class Point2:
    def __init__(self, x, y):
    self.x = x
    self.y = y

    Point is Point2
    Result:
    FalsePass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    point1 = Point(3, 2)
    point1 is Point
    Result:
    FalsePass Icon