Classes and objects¶
~20 min ยท prerequisite: Parts 1 to 4
What you will be able to do¶
- Define a class with an
__init__method that sets up each new object. - Read and change an object's attributes, and call its methods.
- Explain what
selfrefers to inside a method, and check whether a value belongs to a class withisinstance.
The idea¶
So far, you have stored each value in its own variable and passed values to functions. When a program tracks several facts about one thing, such as a product's name, price, and quantity, loose variables are hard to keep together. A class groups related data and the functions that work on that data under one name.
Think of a class as a blueprint and an object as one thing built from it. A house blueprint is not a house. You can build many houses from one blueprint, and each house can have its own paint color. In Python, Product is the blueprint, and each product you create is an instance of Product.
You define a class with the keyword class, followed by a name in CapWords style, such as Product. The indented block under it holds methods. A method is a function defined inside a class. The method named __init__ runs automatically each time you create an object. Its job is to set up the new object's attributes, which are values stored on the object and written as self.name.
The first parameter of every method is self. Python passes the object into it for you. When you write item.total_value(), Python calls the method with item as its first argument. Inside the method, self refers to item, so self.price reads that object's own price. Two products have separate attributes, so changing one does not change the other.
Calling a class looks like calling a function. The expression Product("Pen", 2.5, 10) creates a new object, runs __init__, and returns the object.
Every object knows its class. Use isinstance(item, Product) to check whether item belongs to Product.
Worked example¶
class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
def total_value(self):
return self.price * self.quantity
def restock(self, amount):
self.quantity += amount
pen = Product("Pen", 2.5, 10)
mug = Product("Mug", 8.0, 3)
print(pen.total_value())
pen.restock(5)
print(pen.quantity)
print(mug.total_value())
The output is 25.0, then 15, then 24.0. Line by line, this is what Python does:
class Product:defines a new class namedProduct. The colon starts an indented block.def __init__(self, name, price, quantity):defines the initializer. Python calls it with the new object asself, plus the three values you pass in.- The next three lines store each value as an attribute on
self. After them, the object carries its own name, price, and quantity. total_valuemultiplies its object's price by its quantity and returns the result.restockaddsamountto the quantity of the object it was called on.Product("Pen", 2.5, 10)creates the first object, andProduct("Mug", 8.0, 3)creates a second, separate one.pen.total_value()computes2.5 * 10, which is25.0.pen.restock(5)changes onlypen, so its quantity becomes15.mug.total_value()computes8.0 * 3, which is24.0. The change topendid not touchmug.
Try it¶
The first block checks one object. The second shows that objects keep separate state.
class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
def total_value(self):
return self.price * self.quantity
pen = Product("Pen", 2.5, 10)
assert pen.name == "Pen"
assert pen.total_value() == 25.0
assert type(pen) is Product
assert isinstance(pen, Product)
Common mistakes¶
-
Leaving out
selfin a method. Writingdef total_value():and then callingpen.total_value()gives:Add
selfas the first parameter of every method. -
Forgetting
self.when storing an attribute. Writingname = nameinside__init__only changes a local variable. Thenpen.namegives:Write
self.name = nameinstead. -
Leaving out an argument when you create an object. Writing
Product("Pen", 2.5)gives: -
Forgetting the parentheses on a method. Writing
assert pen.total_value == 25.0does not call the method. It compares a method object with a number, so the check fails:Write
pen.total_value()to call it. -
Reading an attribute that was never set. Writing
pen.colorwhen__init__never setscolorgives:
Practice¶
- Create a class
Bookwhose__init__takestitleandpages. Create a book titled"Dune"with 412 pages, and assert both attributes. - Add a method
pages_left(self, read)toBookthat returns the number of pages not yet read. Assert thatBook("Dune", 412).pages_left(100)is312. - Create a class
Counterwith avalueattribute that starts at0, and a methodincrement(self, step=1)that addssteptovalue. Callincrement()once andincrement(5)once, then assertvalueis6. - Create two
Bookobjects. Change thetitleof one, then assert the other one keeps its original title. - Stretch: create a class
Rectanglewithwidthandheight, a methodarea(), and a methodcan_fit(self, other)that returnsTruewhenotherfits insideself, with width and height each no larger. AssertRectangle(4, 5).can_fit(Rectangle(3, 5))isTrueandRectangle(4, 5).can_fit(Rectangle(5, 1))isFalse.
Solution
- ```python class Book: def init(self, title, pages): self.title = title self.pages = pages
book = Book("Dune", 412) assert book.title == "Dune" assert book.pages == 412 ```
-
```python class Book: def init(self, title, pages): self.title = title self.pages = pages
def pages_left(self, read): return self.pages - read
assert Book("Dune", 412).pages_left(100) == 312 ```
-
```python class Counter: def init(self): self.value = 0
def increment(self, step=1): self.value += step
counter = Counter() counter.increment() counter.increment(5) assert counter.value == 6 ```
- ```python class Book: def init(self, title, pages): self.title = title self.pages = pages
first = Book("Dune", 412) second = Book("Emma", 474) first.title = "Dune Messiah" assert second.title == "Emma" ```
-
```python class Rectangle: def init(self, width, height): self.width = width self.height = height
def area(self): return self.width * self.height
def can_fit(self, other): return other.width <= self.width and other.height <= self.height
assert Rectangle(4, 5).can_fit(Rectangle(3, 5)) is True assert Rectangle(4, 5).can_fit(Rectangle(5, 1)) is False ```
Recap¶
- A class is a blueprint, and each object made from it has its own attributes.
__init__sets those attributes when the object is created. - Every method takes
selfas its first parameter, andselfrefers to the object the method was called on. - Use
isinstanceto check whether an object belongs to a class.