Python classes let you bundle data and behavior. Think of a class as a blueprint. Just like a recipe tells you how to bake a cake, a class tells Python how to build an object.
When you create a class you define two things: attributes and methods. Attributes hold state. Methods perform actions. Here is a tiny example:
class Counter:
count = 0
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
Counter.count += 1
c1 = Counter()
c2 = Counter()
c1.increment()
c2.increment()
print(Counter.count) # likely shows 2
I still remember the first time I wrote a class. I felt like I was giving life to a piece of code. The simple act of defining a method made the program feel more organized.
Attributes can be instance specific or shared. In the example above count is a class attribute. It lives on the class itself. Each instance gets its own value attribute. This distinction matters when you work with mutable objects.
class Dog:
tricks = [] # shared list can be surprising
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
d1 = Dog('Fido')
d2 = Dog('Buddy')
d1.add_trick('roll over')
d2.add_trick('play dead')
print(d1.tricks) # may contain both tricks
The output may contain both tricks because the list is shared. A safer design uses an instance attribute:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # fresh list for each dog
def add_trick(self, trick):
self.tricks.append(trick)
Now each dog keeps its own tricks.
Methods receive the instance as their first argument. Python calls this argument self. The name itself is not magical; it is just a convention. If you ignore the convention your code may be harder for others to read.
class Bag:
def __init__(self):
self.data = []
def add(self, item):
self.data.append(item)
def show(self):
return self.data
bag = Bag()
bag.add('apple')
bag.add('banana')
print(bag.show()) # likely shows ['apple', 'banana']
You can also inherit from a class. Inheritance lets you reuse code and add new behavior. Here is a quick example:
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return 'Woof'
class Cat(Animal):
def speak(self):
return 'Meow'
for pet in (Dog(), Cat()):
print(pet.speak()) # likely prints Woof then Meow
Multiple inheritance is possible but can become complex. Python uses a method resolution order to decide which method to call. In most everyday cases you will stick with a single base class.
Private attributes follow a naming convention. A name that starts with an underscore is considered private. Python does not enforce privacy; it just signals intent. Name mangling can be used to avoid accidental overrides:
class Mapping:
def __init__(self, mapping):
self.items = []
self.__update(mapping)
def update(self, mapping):
for key, value in mapping.items():
self.items.append((key, value))
__update = update # private copy
class Sub(Mapping):
def update(self, keys, values):
for k, v in zip(keys, values):
self.items.append((k, v))
The mangled name changes to keep the method call intact.
You can also create data‑carrying classes with the dataclass decorator. It reduces boilerplate:
from dataclasses import dataclass
@dataclass
class Employee:
name: str
dept: str
salary: int
e = Employee('Jane', 'Engineering', 90000)
print(e.dept) # likely shows Engineering
print(e.salary) # likely shows 90000
That’s it for a quick tour of Python classes. The concepts build on each other. Start with a simple class, play with attributes, then explore inheritance and dataclasses. The journey is rewarding and the community is generous with examples.