Note: Each day this week I’m going to republish one of my most popular posts. My hope is that people who missed them the first time might find them useful now. This post, on “classes” and Object Oriented Programming remains my most popular post of all time. I’ve gotten hundreds of emails about this post alone. Many have found it an accessible introduction to classes and OOP, both in Python and in general. The is a fundamental building block in . It is the underpinning for not only many popular programs and libraries, but the Python standard library as well. Understanding what classes are, when to use them, and how they can be useful is essential, and the goal of this article. In the process, we'll explore what the term means and how it ties together with Python classes. class Python Object-Oriented Programming Everything Is An Object… What is the keyword used for, exactly? Like its function-based cousin , it concerns the of things. While is used to define a function, is used to define a . And what is a class? Simply a logical grouping of data and functions (the latter of which are frequently referred to as "methods" when defined within a class). class def definition def class class What do we mean by “logical grouping”? Well, a class can contain any data we’d like it to, and can have any functions (methods) attached to it that we please. Rather than just throwing random things together under the name “class”, we try to create classes where there is a logical connection between things. Many times, classes are based on objects in the real world (like or ). Other times, classes are based on concepts in our system, like or . Customer Product HTTPRequest Owner Regardless, classes are a technique; a way of thinking about programs. When you think about and implement your system in this way, you’re said to be performing . “Classes” and “objects” are words that are often used interchangeably, but they’re not really the same thing. Understanding what makes them different is the key to understanding what they are and how they work. modeling Object-Oriented Programming ..So Everything Has A Class? Classes can be thought of as . When I a Customer class using the keyword, I haven't actually created a customer. Instead, what I've created is a sort of instruction manual for constructing "customer" objects. Let's look at the following example code: blueprints for creating objects define class (object): class Customer """A customer of ABC Bank with a checking account. Customers have the following properties: _Attributes:_ _name: A string representing the customer's name._ _balance: A float tracking the current balance of the customer's account._ _"""_ **def** \_\_init\_\_(self, name, balance=0.0): _"""Return a Customer object whose name is \*name\* and starting_ _balance is \*balance\*."""_ self.name = name self.balance = balance **def** withdraw(self, amount): _"""Return the balance remaining after withdrawing \*amount\*_ _dollars."""_ **if** amount > self.balance: **raise** **RuntimeError**('Amount greater than available balance.') self.balance -= amount **return** self.balance **def** deposit(self, amount): _"""Return the balance remaining after depositing \*amount\*_ _dollars."""_ self.balance += amount **return** self.balance The line create a new customer. That is, just because we've a doesn't mean we've one; we've merely outlined the to create a object. To do so, we call the class's method with the proper number of arguments (minus , which we'll get to in a moment). class Customer(object) does not defined Customer created blueprint Customer __init__ self So, to use the “blueprint” that we created by defining the (which is used to create objects), we call the class name almost as if it were a function: . This line simply says "use the blueprint to create me a new object, which I'll refer to as ." class Customer Customer jeff = Customer('Jeff Knupp', 1000.0) Customer jeff The , known as an , is the realized version of the . Before we called , no object existed. We can, of course, create as many objects as we'd like. There is still, however, only one , regardless of how many of the class we create. jeff object instance Customer class Customer() Customer Customer Customer class instances ? self So what’s with that parameter to all of the methods? What is it? Why, it's the instance, of course! Put another way, a method like defines the instructions for withdrawing money from . Calling puts those instructions to use . self Customer withdraw some abstract customer's account jeff.withdraw(100.0) on the _jeff_ instance So when we say , we're saying, "here's how you withdraw money from a Customer object (which we'll call ) and a dollar figure (which we'll call ). is the of the that is being called on. That's not me making analogies, either. is just shorthand for , which is perfectly valid (if not often seen) code. def withdraw(self, amount): self amount self instance Customer withdraw jeff.withdraw(100.0) Customer.withdraw(jeff, 100.0) __init__ may make sense for other methods, but what about ? When we call , we're in the process of creating an object, so how can there already be a ? Python allows us to extend the pattern to when objects are constructed as well, even though it doesn't fit. Just imagine that is the same as calling ; the that's passed in is also made the result. self __init__ __init__ self self exactly jeff = Customer('Jeff Knupp', 1000.0) jeff = Customer(jeff, 'Jeff Knupp', 1000.0) jeff This is why when we call , we objects by saying things like . Remember, since the instance, this is equivalent to saying , which is the same as . Similarly, is the same as . After these two lines, we consider the object "initialized" and ready for use. __init__ initialize self.name = name self is jeff.name = name jeff.name = 'Jeff Knupp self.balance = balance jeff.balance = 1000.0 Customer Be careful what you __init__ After has finished, the caller can rightly assume that the object is ready to use. That is, after , we can start making and calls on ; is a object. __init__ jeff = Customer('Jeff Knupp', 1000.0) deposit withdraw jeff jeff fully-initialized Imagine for a moment we had defined the class slightly differently: Customer (object): class Customer """A customer of ABC Bank with a checking account. Customers have the following properties: _Attributes:_ _name: A string representing the customer's name._ _balance: A float tracking the current balance of the customer's account._ _"""_ **def** \_\_init\_\_(self, name): _"""Return a Customer object whose name is \*name\*."""_ self.name = name **def** set\_balance(self, balance=0.0): _"""Set the customer's starting balance."""_ self.balance = balance **def** withdraw(self, amount): _"""Return the balance remaining after withdrawing \*amount\*_ _dollars."""_ **if** amount > self.balance: **raise** **RuntimeError**('Amount greater than available balance.') self.balance -= amount **return** self.balance **def** deposit(self, amount): _"""Return the balance remaining after depositing \*amount\*_ _dollars."""_ self.balance += amount **return** self.balance This may look like a reasonable alternative; we simply need to call before we begin using the instance. There's no way, however, to communicate this to the caller. Even if we document it extensively, we can't the caller to call before calling . Since the instance doesn't even a balance attribute until is called, this means that the object hasn't been "fully" initialized. set_balance force jeff.set_balance(1000.0) jeff.withdraw(100.0) jeff have jeff.set_balance The rule of thumb is, don’t a new attribute outside of the method, otherwise you've given the caller an object that isn't fully initialized. There are exceptions, of course, but it's a good principle to keep in mind. This is part of a larger concept of object : there shouldn't be any series of method calls that can result in the object entering a state that doesn't make sense. introduce __init__ consistency Invariants (like, “balance should always be a non-negative number”) should hold both when a method is entered and when it is exited. It should be impossible for an object to get into an invalid state just by calling its methods. It goes without saying, then, that an object should in a valid state as well, which is why it’s important to initialize everything in the method. start __init__ Instance Attributes and Methods An function defined in a class is called a “method”. Methods have access to all the data contained on the instance of the object; they can access and modify anything previously set on . Because they use , they require an instance of the class in order to be used. For this reason, they're often referred to as "instance methods". self self If there are “instance methods”, then surely there are other types of methods as well, right? Yes, there are, but these methods are a bit more esoteric. We’ll cover them briefly here, but feel free to research these topics in more depth. Static Methods are attributes that are set at the , as opposed to the . Normal attributes are introduced in the method, but some attributes of a class hold for instances in all cases. For example, consider the following definition of a object: Class attributes class-level instance-level __init__ all Car (object): class Car wheels = 4 **def** \_\_init\_\_(self, make, model): self.make = make self.model = model mustang = Car('Ford', 'Mustang') mustang.wheels Car.wheels print # 4 print # 4 A always has four , regardless of the or . Instance methods can access these attributes in the same way they access regular attributes: through (i.e. ). Car wheels make model self self.wheels There is a class of methods, though, called , that don’t have access to . Just like class attributes, they are methods that work without requiring an instance to be present. Since instances are always referenced through , static methods have no parameter. static methods self self self The following would be a valid static method on the class: Car (object):... make_car_sound(): 'VRooooommmm!' class Car def print No matter what kind of car we have, it always makes the same sound (or so I tell my ten month old daughter). To make it clear that this method should not receive the instance as the first parameter (i.e. on "normal" methods), the decorator is used, turning our definition into: self @staticmethod (object):...@staticmethod make_car_sound(): 'VRooooommmm!' class Car def print Class Methods A variant of the static method is the . Instead of receiving the as the first parameter, it is passed the . It, too, is defined using a decorator: class method instance class (object):...@classmethod is_motorcycle(cls): cls.wheels == 2 class Vehicle def return Class methods may not make much sense right now, but that’s because they’re used most often in connection with our next topic: . inheritance Inheritance While Object-oriented Programming is useful as a modeling tool, it truly gains power when the concept of is introduced. is the process by which a “child” class the data and behavior of a “parent” class. An example will definitely help us here. inheritance Inheritance derives Imagine we run a car dealership. We sell all types of vehicles, from motorcycles to trucks. We set ourselves apart from the competition by our prices. Specifically, how we determine the price of a vehicle on our lot: $5,000 x number of wheels a vehicle has. We love buying back our vehicles as well. We offer a flat rate — 10% of the miles driven on the vehicle. For trucks, that rate is $10,000. For cars, $8,000. For motorcycles, $4,000. If we wanted to create a sales system for our dealership using Object-oriented techniques, how would we do so? What would the objects be? We might have a class, a class, an class, and so forth, but we'd almost certainly have a , , and class. Sale Customer Inventory Car Truck Motorcycle What would these classes look like? Using what we’ve learned, here’s a possible implementation of the class: Car (object): class Car """A car for sale by Jeffco Car Dealership. _Attributes:_ _wheels: An integer representing the number of wheels the car has._ _miles: The integral number of miles driven on the car._ _make: The make of the car as a string._ _model: The model of the car as a string._ _year: The integral year the car was built._ _sold\_on: The date the vehicle was sold._ _"""_ **def** \_\_init\_\_(self, wheels, miles, make, model, year, sold\_on): _"""Return a new Car object."""_ self.wheels = wheels self.miles = miles self.make = make self.model = model self.year = year self.sold\_on = sold\_on **def** sale\_price(self): _"""Return the sale price for this car as a float amount."""_ **if** self.sold\_on **is** **not** None: **return** 0.0 _\# Already sold_ **return** 5000.0 \* self.wheels **def** purchase\_price(self): _"""Return the price for which we would pay to purchase the car."""_ **if** self.sold\_on **is** None: **return** 0.0 _\# Not yet sold_ **return** 8000 - (.10 \* self.miles) ... OK, that looks pretty reasonable. Of course, we would likely have a number of other methods on the class, but I’ve shown two of particular interest to us: and . We'll see why these are important in a bit. sale_price purchase_price Now that we’ve got the class, perhaps we should crate a class? Let's follow the same pattern we did for car: Car Truck (object): class Truck """A truck for sale by Jeffco Car Dealership. _Attributes:_ _wheels: An integer representing the number of wheels the truck has._ _miles: The integral number of miles driven on the truck._ _make: The make of the truck as a string._ _model: The model of the truck as a string._ _year: The integral year the truck was built._ _sold\_on: The date the vehicle was sold._ _"""_ **def** \_\_init\_\_(self, wheels, miles, make, model, year, sold\_on): _"""Return a new Truck object."""_ self.wheels = wheels self.miles = miles self.make = make self.model = model self.year = year self.sold\_on = sold\_on **def** sale\_price(self): _"""Return the sale price for this truck as a float amount."""_ **if** self.sold\_on **is** **not** None: **return** 0.0 _\# Already sold_ **return** 5000.0 \* self.wheels **def** purchase\_price(self): _"""Return the price for which we would pay to purchase the truck."""_ **if** self.sold\_on **is** None: **return** 0.0 _\# Not yet sold_ **return** 10000 - (.10 \* self.miles) ... Wow. That’s to the car class. One of the most important rules of programming (in general, not just when dealing with objects) is “DRY” or “ on’t epeat ourself. We’ve definitely repeated ourselves here. In fact, the and classes differ only by (aside from comments). almost identical D R Y Car Truck a single character So what gives? Where did we go wrong? Our main problem is that we raced straight to the concrete: s and s are real things, tangible objects that make intuitive sense as classes. However, they share so much data and functionality in common that it seems there must be an we can introduce here. Indeed there is: the notion of s. Car Truck abstraction Vehicle Abstract Classes A is not a real-world object. Rather, it is a that some real-world objects (like cars, trucks, and motorcycles) embody. We would like to use the fact that each of these objects can be considered a vehicle to remove repeated code. We can do that by creating a class: Vehicle concept Vehicle class Vehicle(object):"""A vehicle for sale by Jeffco Car Dealership. Attributes: wheels: An integer representing the number of wheels the vehicle has. miles: The integral number of miles driven on the vehicle. make: The make of the vehicle as a string. model: The model of the vehicle as a string. year: The integral year the vehicle was built. sold\_on: The date the vehicle was sold. """ base\_sale\_price = 0 def \_\_init\_\_(self, wheels, miles, make, model, year, sold\_on): """Return a new Vehicle object.""" self.wheels = wheels self.miles = miles self.make = make self.model = model self.year = year self.sold\_on = sold\_on def sale\_price(self): """Return the sale price for this vehicle as a float amount.""" **if** self.sold\_on is not None: **return** 0.0 # Already sold **return** 5000.0 \* self.wheels def purchase\_price(self): """Return the price for which we would pay to purchase the vehicle.""" **if** self.sold\_on is None: **return** 0.0 # Not yet sold **return** self.base\_sale\_price - (.10 \* self.miles) Now we can make the and class from the class by replacing in the line . The class in parenthesis is the class that is inherited from ( essentially means "no inheritance". We'll discuss exactly why we write that in a bit). Car Truck inherit Vehicle object class Car(object) object We can now define and in a very straightforward way: Car Truck (Vehicle): class Car **def** \_\_init\_\_(self, wheels, miles, make, model, year, sold\_on): _"""Return a new Car object."""_ self.wheels = wheels self.miles = miles self.make = make self.model = model self.year = year self.sold\_on = sold\_on self.base\_sale\_price = 8000 (Vehicle): class Truck **def** \_\_init\_\_(self, wheels, miles, make, model, year, sold\_on): _"""Return a new Truck object."""_ self.wheels = wheels self.miles = miles self.make = make self.model = model self.year = year self.sold\_on = sold\_on self.base\_sale\_price = 10000 This works, but has a few problems. First, we’re still repeating a lot of code. We’d ultimately like to get rid of repetition. Second, and more problematically, we’ve introduced the class, but should we really allow people to create objects (as opposed to s or s)? A is just a concept, not a real thing, so what does it mean to say the following: all Vehicle Vehicle Car Truck Vehicle v = Vehicle(4, 0, 'Honda', 'Accord', 2014, None)print v.purchase_price() A doesn't have a , only the individual classes like and do. The issue is that should really be an . Abstract Base Classes are classes that are only meant to be inherited from; you can't create of an ABC. That means that, if is an ABC, the following is illegal: Vehicle base_sale_price child Car Truck Vehicle Abstract Base Class instance Vehicle v = Vehicle(4, 0, 'Honda', 'Accord', 2014, None) It makes sense to disallow this, as we never meant for vehicles to be used directly. We just wanted to use it to abstract away some common data and behavior. So how do we make a class an ABC? Simple! The module contains a metaclass called (metaclasses are a bit outside the scope of this article). Setting a class's metaclass to and making one of its methods makes it an ABC. A method is one that the ABC says must exist in child classes, but doesn't necessarily actually implement. For example, the Vehicle class may be defined as follows: abc ABCMeta ABCMeta virtual virtual ABCMeta, abstractmethod from abc import (object): class Vehicle """A vehicle for sale by Jeffco Car Dealership. _Attributes:_ _wheels: An integer representing the number of wheels the vehicle has._ _miles: The integral number of miles driven on the vehicle._ _make: The make of the vehicle as a string._ _model: The model of the vehicle as a string._ _year: The integral year the vehicle was built._ _sold\_on: The date the vehicle was sold._ _"""_ \_\_metaclass\_\_ = ABCMeta base\_sale\_price = 0 **def** sale\_price(self): _"""Return the sale price for this vehicle as a float amount."""_ **if** self.sold\_on **is** **not** None: **return** 0.0 _\# Already sold_ **return** 5000.0 \* self.wheels **def** purchase\_price(self): _"""Return the price for which we would pay to purchase the vehicle."""_ **if** self.sold\_on **is** None: **return** 0.0 _\# Not yet sold_ **return** self.base\_sale\_price - (.10 \* self.miles) @abstractmethod **def** vehicle\_type(): _""""Return a string representing the type of vehicle this is."""_ **pass** Now, since is an , we can't directly create an instance of . As long as and inherit from define , we can instantiate those classes just fine. vehicle_type abstractmethod Vehicle Car Truck Vehicle and vehicle_type Returning to the repetition in our and classes, let see if we can't remove that by hoisting up common functionality to the base class, : Car Truck Vehicle ABCMeta, abstractmethod (object): from abc import class Vehicle """A vehicle for sale by Jeffco Car Dealership. _Attributes:_ _wheels: An integer representing the number of wheels the vehicle has._ _miles: The integral number of miles driven on the vehicle._ _make: The make of the vehicle as a string._ _model: The model of the vehicle as a string._ _year: The integral year the vehicle was built._ _sold\_on: The date the vehicle was sold._ _"""_ \_\_metaclass\_\_ = ABCMeta base\_sale\_price = 0 wheels = 0 **def** \_\_init\_\_(self, miles, make, model, year, sold\_on): self.miles = miles self.make = make self.model = model self.year = year self.sold\_on = sold\_on **def** sale\_price(self): _"""Return the sale price for this vehicle as a float amount."""_ **if** self.sold\_on **is** **not** None: **return** 0.0 _\# Already sold_ **return** 5000.0 \* self.wheels **def** purchase\_price(self): _"""Return the price for which we would pay to purchase the vehicle."""_ **if** self.sold\_on **is** None: **return** 0.0 _\# Not yet sold_ **return** self.base\_sale\_price - (.10 \* self.miles) @abstractmethod **def** vehicle\_type(self): _""""Return a string representing the type of vehicle this is."""_ **pass** Now the and classes become: Car Truck (Vehicle): class Car """A car for sale by Jeffco Car Dealership.""" base\_sale\_price = 8000 wheels = 4 **def** vehicle\_type(self): _""""Return a string representing the type of vehicle this is."""_ **return** 'car' (Vehicle): class Truck """A truck for sale by Jeffco Car Dealership.""" base\_sale\_price = 10000 wheels = 4 **def** vehicle\_type(self): _""""Return a string representing the type of vehicle this is."""_ **return** 'truck' This fits perfectly with our intuition: as far as our system is concerned, the only difference between a car and truck is the base sale price. Defining a class, then, is similarly simple: Motorcycle (Vehicle): class Motorcycle """A motorcycle for sale by Jeffco Car Dealership.""" base\_sale\_price = 4000 wheels = 2 **def** vehicle\_type(self): _""""Return a string representing the type of vehicle this is."""_ **return** 'motorcycle' Inheritance and the LSP Even though it seems like we used inheritance to get rid of duplication, what we were doing was simply providing the proper level of abstraction. And is the key to understanding inheritance. We’ve seen how one side-effect of using inheritance is that we reduce duplicated code, but what about from the . How does using inheritance change that code? really abstraction caller’s perspective Quite a bit, it turns out. Imagine we have two classes, and , and we want to write a function that takes either type of object and prints out whether or not the instance in question can speak (a dog can't, a person can). We might write code like the following: Dog Person can_speak(animal): isinstance(animal, Person): True isinstance(animal, Dog): False : ('Unknown animal!') def if return elif return else raise RuntimeError That works when we only have two types of animals, but what if we have twenty, or ? That chain is going to get quite long. two hundred if...elif The key insight here is that shouldn't care what type of animal it's dealing with, the animal class itself should tell if it can speak. By introducing a common base class, , that defines , we relieve the function of it's type-checking burden. Now, as long as it knows it was an that was passed in, determining if it can speak is trivial: can_speak us Animal can_speak Animal can_speak(animal): animal.can_speak() def return This works because and (and whatever other classes we crate to derive from ) follow the . This states that we should be able to use a child class (like or ) wherever a parent class ( ) is expected an everything will work fine. This sounds simple, but it is the basis for a powerful concept we'll discuss in a future article: . Person Dog Animal Liskov Substitution Principle Person Dog Animal interfaces Summary Hopefully, you’ve learned a lot about what Python classes are, why they’re useful, and how to use them. The topic of classes and Object-oriented Programming are insanely deep. Indeed, they reach to the core of computer science. This article is not meant to be an exhaustive study of classes, nor should it be your only reference. There are literally thousands of explanations of OOP and classes available online, so if you didn’t find this one suitable, certainly a bit of searching will reveal one better suited to you. As always, corrections and arguments are welcome in the comments. Just try to keep it civil. Posted on Mar 27, 2017 by Jeff Knupp Originally published at jeffknupp.com on March 27, 2017. is how hackers start their afternoons. We’re a part of the family. We are now and happy to opportunities. Hacker Noon @AMI accepting submissions discuss advertising & sponsorship To learn more, , , or simply, read our about page like/message us on Facebook tweet/DM @HackerNoon. If you enjoyed this story, we recommend reading our and . Until next time, don’t take the realities of the world for granted! latest tech stories trending tech stories