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
Userclass. Python's indentation-based syntax requires us to put something inside of the class, so we usepass.passis a placeholder keyword intended for exactly this type of situation.>
class User:passOnce we have a
Userclass, 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 arelower_snake_cased.UserDatabaseis a class, butuser_databaseis a function or variable.Our
Userclass 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.nameResult:
'Amir'
Each instance of a class is distinct and tracks its own attributes.
>
class User:passamir = User()amir.name = "Amir"betty = User()betty.name = "Betty"- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
amir.nameResult:
'Amir'
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
betty.nameResult:
'Betty'
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 = nameamir = User("Amir")amir.nameResult:
'Amir'
All methods, including
.__init__, get the instance as their first argument. We name that argumentself. It lets us interact with the instance from inside the method, like theself.name = nameline above.Unlike many programming languages, Python has no built-in
thisorselfkeyword. Theselfargument above is just a regular function argument!To prove that
selfisn'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 = nameamir = User("Amir")amir.nameResult:
'Amir'
To recap, when we instantiate a user with
User("Amir"):- Python creates a new instance of
User, calledself. - Python calls the initializer via
self.__init__("Amir"). - Python returns
selfto us.
- Python creates a new instance of
We can define additional instance methods on our class. Each one takes
selfas an explicit argument. But when we call a method, likekeanu.name_length()below, we don't have to passselfin. Python automatically passes theselfargument for us.>
class Cat:def __init__(self, name):self.name = namedef name_length(self):return len(self.name)keanu = Cat("Keanu")keanu.name_length()Result:
5
Some programming languages let us refer to attributes directly, for example with
len(name)instead oflen(self.name). In Python, theself.is mandatory. Directly referencingnameinstead ofself.nameraises aNameError, since there's nonamevariable in scope.>
class Cat:def __init__(self, name):self.name = namedef name_length(self):return len(name)keanu = Cat("Keanu")keanu.name_length()Result:
NameError: name 'name' is not defined
We also have to explicitly assign attributes in the initializer, like
self.name = nameabove. If the initializer takes an argument, but doesn't use it, then it has no effect.In the next example, the initializer takes
ageas an argument, but it never assigns it toself.age. When the.is_kittenmethod accessesself.age, it raises anAttributeErrorbecause that attribute doesn't exist.>
class Cat:def __init__(self, name, age):# Note that we do nothing with `age`.self.name = namedef is_kitten(self):return self.age <= 2keanu = Cat("Keanu", 2)keanu.is_kitten()Result:
AttributeError: 'Cat' object has no attribute 'age'
Methods, including
.__init__, are functions. They can use function features like default argument values.>
class Cat:def __init__(self, name, age=2):self.name = nameself.age = agedef is_kitten(self):return self.age <= 2keanu = Cat("Keanu")ms_fluff = Cat("Ms. Fluff", 4)[keanu.is_kitten(), ms_fluff.is_kitten()]Result:
[True, False]
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
Catclass slightly differently. Instead of accepting the cat's age as a constructor argument, the class might expect us to set theageattribute manually after instantiating it. But that's dangerous: if we forget to set theageattribute, then any code trying to access that attribute raises anAttributeError.>
class Cat:def __init__(self, name):self.name = namedef is_kitten(self):return self.age <= 2- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
keanu = Cat("Keanu")keanu.age = 2keanu.is_kitten()Result:
True
- 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'
Instances are mutable, so we can modify existing attributes after the instance is instantiated.
>
class Cat:def __init__(self, name, age):self.name = nameself.age = agedef is_kitten(self):return self.age <= 2keanu = Cat("Keanu", 2)is_kitten_this_year = keanu.is_kitten()keanu.age += 1is_kitten_next_year = keanu.is_kitten()[is_kitten_this_year, is_kitten_next_year]Result:
[True, False]
Here's a code problem:
Define a
Catclass whose instances have anameattribute. The constructor should take the cat's name as an argument.Add a
.name_ends_in_ymethod that returnsTruewhen the cat's name ends in "y". To check for whether a string ends in y, callsome_string.endswith("y").class Cat:def __init__(self, name):self.name = namedef name_ends_in_y(self):return self.name.endswith("y")assert Cat("Keanu").name_ends_in_y() == Falseassert Cat("Grumpy").name_ends_in_y() == True- Goal:
No errors.
- Yours:
No errors.
In an earlier lesson, we used
isinstanceto check a value against a type.>
greeting = "hello"isinstance(greeting, str)Result:
True
This works with our own classes as well.
>
class Point:def __init__(self, x, y):self.x = xself.y = y- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
point1 = Point(3, 2)isinstance(point1, Point)Result:
True
The
isoperator checks object identity: are these two objects exactly the same, living at the same location in memory? A given instanceisitself, and a class alsoisitself. However:- a given instance
is notanother instance - a class
is notanother class - an instance
is notits own class.
- a given instance
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
point1 = Point(3, 2)point2 = point1point2 is point1Result:
True
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
# We can create another variable referencing a class!Point2 = PointPoint2 is PointResult:
True
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
point1 = Point(3, 2)point2 = Point(3, 2)point1 is point2Result:
False
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
class Point2:def __init__(self, x, y):self.x = xself.y = yPoint is Point2Result:
False
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
point1 = Point(3, 2)point1 is PointResult:
False