Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Inheritance and Internals: Object-Oriented Programming in Python
Python supports the object-oriented programming paradigm through classes. They provide an elegant way to define reusable pieces of code that encapsulate data and behavior in a single entity. With classes, you can quickly and intuitively model real-world objects and solve complex problems.
If you’re new to classes, need to refresh your knowledge, or want to dive deeper into them, then this tutorial is for you!
In this tutorial, you’ll learn how to:
- Define Python classes with the
classkeyword - Add state to your classes with class and instance attributes
- Provide behavior to your classes with methods
- Use inheritance to build hierarchies of classes
- Provide interfaces with abstract classes
To get the most out of this tutorial, you should know about Python variables, data types, and functions. Some experience with object-oriented programming (OOP) is also a plus. Don’t worry if you’re not an OOP expert yet. In this tutorial, you’ll learn the key concepts that you need to get started and more. You’ll also write several practical examples to help reinforce your knowledge of Python classes.
Free Bonus: Click here to download your sample code for building powerful object blueprints with classes in Python.
Getting Started With Python Classes
Python is a multiparadigm programming language that supports object-oriented programming (OOP) through classes that you can define with the class keyword. You can think of a class as a piece of code that specifies the data and behavior that represent and model a particular type of object.
What is a class in Python? A common analogy is that a class is like the blueprint for a house. You can use the blueprint to create several houses and even a complete neighborhood. Each concrete house is an object or instance that’s derived from the blueprint.
Each instance can have its own properties, such as color, owner, and interior design. These properties carry what’s commonly known as the object’s state. Instances can also have different behaviors, such as locking the doors and windows, opening the garage door, turning the lights on and off, watering the garden, and more.
In OOP, you commonly use the term attributes to refer to the properties or data associated with a specific object of a given class. In Python, attributes are variables defined inside a class with the purpose of storing all the required data for the class to work.
Similarly, you’ll use the term methods to refer to the different behaviors that objects will show. Methods are functions that you define within a class. These functions typically operate on or with the attributes of the underlying instance or class. Attributes and methods are collectively referred to as members of a class or object.
You can write fully functional classes to model the real world. These classes will help you better organize your code and solve complex programming problems.
For example, you can use classes to create objects that emulate people, animals, vehicles, books, buildings, cars, or other objects. You can also model virtual objects, such as a web server, directory tree, chatbot, file manager, and more.
Finally, you can use classes to build class hierarchies. This way, you’ll promote code reuse and remove repetition throughout your codebase.
In this tutorial, you’ll learn a lot about classes and all the cool things that you can do with them. To kick things off, you’ll start by defining your first class in Python. Then you’ll dive into other topics related to instances, attributes, and methods.
Defining a Class in Python
To define a class, you need to use the class keyword followed by the class name and a colon, just like you’d do for other compound statements in Python. Then you must define the class body, which will start at the next indentation level:
class ClassName:
# Class body
pass
In a class body, you can define attributes and methods as needed. As you already learned, attributes are variables that hold the class data, while methods are functions that provide behavior and typically act on the class data.
Note: In Python, the body of a given class works as a namespace where attributes and methods live. You can only access those attributes and methods through the class or its objects.
As an example of how to define attributes and methods, say that you need a Circle class to model different circles in a drawing application. Initially, your class will have a single attribute to hold the radius. It’ll also have a method to calculate the circle’s area:
# circle.py
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def calculate_area(self):
return round(math.pi * self.radius ** 2, 2)
In this code snippet, you define Circle using the class keyword. Inside the class, you write two methods. The .__init__() method has a special meaning in Python classes. This method is known as the object initializer because it defines and sets the initial values for your attributes. You’ll learn more about this method in the Instance Attributes section.
The second method of Circle is conveniently named .calculate_area() and will compute the area of a specific circle by using its radius. It’s common for method names to contain a verb, such as calculate, to describe an action the method performs. In this example, you’ve used the math module to access the pi constant as it’s defined in that module.
Note: In Python, the first argument of most methods is self. This argument holds a reference to the current object so that you can use it inside the class. You’ll learn more about this argument in the section on instance methods with self.
Cool! You’ve written your first class. Now, how can you use this class in your code to represent several concrete circles? Well, you need to instantiate your class to create specific circle objects from it.
Creating Objects From a Class in Python
The action of creating concrete objects from an existing class is known as instantiation. With every instantiation, you create a new object of the target class. To get your hands dirty, go ahead and make a couple of instances of Circle by running the following code in a Python REPL session:
>>> from circle import Circle
>>> circle_1 = Circle(42)
>>> circle_2 = Circle(7)
>>> circle_1
<__main__.Circle object at 0x102b835d0>
>>> circle_2
<__main__.Circle object at 0x1035e3910>
To create an object of a Python class like Circle, you must call the Circle() class constructor with a pair of parentheses and a set of appropriate arguments. What arguments? In Python, the class constructor accepts the same arguments as the .__init__() method. In this example, the Circle class expects the radius argument.
Calling the class constructor with different argument values will allow you to create different objects or instances of the target class. In the above example, circle_1 and circle_2 are separate instances of Circle. In other words, they’re two different and concrete circles, as you can conclude from the code’s output.
Great! You already know how to create objects of an existing class by calling the class constructor with the required arguments. Now, how can you access the attributes and methods of a given class? That’s what you’ll learn in the next section.
Accessing Attributes and Methods
In Python, you can access the attributes and methods of an object by using dot notation with the dot operator. The following snippet of code shows the required syntax:
obj.attribute_name
obj.method_name()
Note that the dot (.) in this syntax basically means give me the following attribute or method from this object. The first line returns the value stored in the target attribute, while the second line accesses the target method so that you can call it.
Note: Remember that to call a function or method, you need to use a pair of parentheses and a series of arguments, if applicable.
Now get back to your circle objects and run the following code:
>>> from circle import Circle
>>> circle_1 = Circle(42)
>>> circle_2 = Circle(7)
>>> circle_1.radius
42
>>> circle_1.calculate_area()
5541.77
>>> circle_2.radius
7
>>> circle_2.calculate_area()
153.94
In the first couple of lines after the instantiation, you access the .radius attribute on your circle_1 object. Then you call the .calculate_area() method to calculate the circle’s area. In the second pair of statements, you do the same but on the circle_2 object.
You can also use dot notation and an assignment statement to change the current value of an attribute:
>>> circle_1.radius = 100
>>> circle_1.radius
100
>>> circle_1.calculate_area()
31415.93
Now the radius of circle_1 is entirely different. When you call .calculate_area(), the result immediately reflects this change. You’ve changed the object’s internal state or data, which typically impacts its behaviors or methods.
Naming Conventions in Python Classes
Before continuing diving into classes, you’ll need to be aware of some important naming conventions that Python uses in the context of classes. Python is a flexible language that loves freedom and doesn’t like to have explicit restrictions. Because of that, the language and the community rely on conventions rather than restrictions.
Note: Most Python programmers follow the snake_case naming convention, which involves using underscores (_) to separate multiple words. However, the recommended naming convention for Python classes is the PascalCase, where each word is capitalized.
In the following two sections, you’ll learn about two important naming conventions that apply to class attributes.
Public vs Non-Public Members
The first naming convention that you need to know about is related to the fact that Python doesn’t distinguish between private, protected, and public attributes like Java and other languages do. In Python, all attributes are accessible in one way or another. However, Python has a well-established naming convention that you should use to communicate that an attribute or method isn’t intended for use from outside its containing class or object.
The naming convention consists of adding a leading underscore to the member’s name. So, in a Python class, you’ll have the following convention:
| Member | Naming | Examples |
|---|---|---|
| Public | Use the normal naming pattern. | radius, calculate_area() |
| Non-public | Include a leading underscore in names. | _radius, _calculate_area() |
Public members are part of the official interface or API of your classes, while non-public members aren’t intended to be part of that API. This means that you shouldn’t use non-public members outside their defining class.
It’s important to note that the second naming convention only indicates that the attribute isn’t intended to be used directly from outside the containing class. It doesn’t prevent direct access, though. For example, you can run obj._name, and you’ll access the content of ._name. However, this is bad practice, and you should avoid it.
Non-public members exist only to support the internal implementation of a given class and may be removed at any time, so you shouldn’t rely on them. The existence of these members depends on how the class is implemented. So, you shouldn’t use them directly in client code. If you do, then your code could break at any moment.
When writing classes, sometimes it’s hard to decide if an attribute should be public or non-public. This decision will depend on how you want your users to use your classes. In most cases, attributes should be non-public to guarantee the safe use of your classes. A good approach will be to start with all your attributes as non-public and only make them public if real use cases appear.
Name Mangling
Another naming convention that you can see and use in Python classes is to add two leading underscores to attribute and method names. This naming convention triggers what’s known as name mangling.
Name mangling is an automatic name transformation that prepends the class’s name to the member’s name, like in _ClassName__attribute or _ClassName__method. This results in name hiding. In other words, mangled names aren’t available for direct access. They’re not part of a class’s public API.
For example, consider the following sample class:
>>> class SampleClass:
... def __init__(self, value):
... self.__value = value
... def __method(self):
... print(self.__value)
...
>>> sample_instance = SampleClass("Hello!")
>>> vars(sample_instance)
{'_SampleClass__value': 'Hello!'}
>>> vars(SampleClass)
mappingproxy({
...
'__init__': <function SampleClass.__init__ at 0x105dfd4e0>,
'_SampleClass__method': <function SampleClass.__method at 0x105dfd760>,
'__dict__': <attribute '__dict__' of 'SampleClass' objects>,
...
})
>>> sample_instance = SampleClass("Hello!")
>>> sample_instance.__value
Traceback (most recent call last):
...
AttributeError: 'SampleClass' object has no attribute '__value'
>>> sample_instance.__method()
Traceback (most recent call last):
...
AttributeError: 'SampleClass' object has no attribute '__method'
In this class, .__value and .__method() have two leading underscores, so their names are mangled to ._SampleClass__value and ._SampleClass__method(), as you can see in the highlighted lines. Python has automatically added the prefix _SampleClass to both names. Because of this internal renaming, you can’t access the attributes from outside the class using their original names. If you try to do it, then you get an AttributeError.
Note: In the above example, you use the built-in vars() function, which returns a dictionary of all the members associated with the given object. This dictionary plays an important role in Python classes. You’ll learn more about it in the .__dict__ attribute section.
This internal behavior hides the names, creating the illusion of a private attribute or method. However, they’re not strictly private. You can access them through their mangled names:
>>> sample_instance._SampleClass__value
'Hello!'
>>> sample_instance._SampleClass__method()
Hello!
It’s still possible to access named-mangled attributes or methods using their mangled names, although this is bad practice, and you should avoid it in your code. If you see a name that uses this convention in someone’s code, then don’t try to force the code to use the name from outside its containing class.
Name mangling is particularly useful when you want to ensure that a given attribute or method won’t get accidentally overwritten. It’s a way to avoid naming conflicts between classes or subclasses. It’s also useful to prevent subclasses from overriding methods that have been optimized for better performance.
Wow! Up to this point, you’ve learned the basics of Python classes and a bit more. You’ll dive deeper into how Python classes work in a moment. But first, it’s time to jump into some reasons why you should learn about classes and use them in your Python projects.
Understanding the Benefits of Using Classes in Python
Is it worth using classes in Python? Absolutely! Classes are the building blocks of object-oriented programming in Python. They allow you to leverage the power of Python while writing and organizing your code. By learning about classes, you’ll be able to take advantage of all the benefits that they provide. With classes, you can: