Classes

Python - A Quick Start for existing Programers

3 min read

Published Sep 16 2025, updated Sep 30 2025


21
0
0
0

Python

Python supports OOP and classes:

  • Defined by the class keyword.
  • Structure is indented.
  • Top level variables are class attributes available on all instances of a class.
  • Constructer is defined as a function with the name __init__(self, ...).
    • Self object is used to set instance variables: self.my_instance_variable = 4.
    • Constructer parameters can be passed in to the init function: __init__(self, my_parameter1).
    • Inside the init function set self.my_instance_variable = my_parameter1 to set instance variables.
  • Top level functions are instance functions by default and can access self.

Example:

class Dog:
    """ Doc string (multi-line comment) to define what the class does, shows up in intellisense in IDE """

    # Class attribute (shared by all dogs)
    species = "Canis familiaris"

    # Constructor (initializer)
    def __init__(self, name, age):
        # instance attributes
        self.name = name
        self.age = age

    # Instance method
    def bark(self):
        return f"{self.name} says Woof!"

# Create objects (instances)
dog1 = Dog("Rocky", 5)
dog2 = Dog("Luna", 3)

print(dog1.name)
# Rocky
print(dog1.age)
# 5
print(dog1.species)
# Canis familiaris
print(dog1.bark())
# Rocky says Woof!

print(dog2.name)
# Luna
print(dog2.age)
# 3
print(dog2.species)
# Canis familiaris
print(dog2.bark())
# Luna says Woof!

print(Dog.species)
# Canis familiaris




Class methods

Instance Method (default):

  • Normal methods in a class.
  • Take self as the first argument.
  • Can access instance attributes and class attributes.
  • Is called on an individual instance of a class.
class Demo:
    def instance_method(self):
        return f"Called instance_method on {self}"


Class Method (@classmethod):

  • Declared with @classmethod decorator.
  • First parameter is cls (the class itself, not an instance).
  • Can access/modify class attributes but not instance attributes (unless given an instance).
  • Often used as factory methods (alternate constructors).
  • Is called on the actual class record.
class Demo:
    counter = 0

    def __init__(self, value):
        self.value = value
        Demo.counter += 1

    @classmethod
    def how_many(cls):
        # cls refers to the class
        return f"There are {cls.counter} instances."

    @classmethod
    def from_string(cls, value_str):
        # factory method
        return cls(int(value_str))

a = Demo(10)
b = Demo.from_string("20")
# create instance from string
print(Demo.how_many())
# "There are 2 instances."

Static Method (@staticmethod):

  • Declared with @staticmethod decorator.
  • No self or cls parameter → acts like a regular function, but lives inside the class for organisation.
  • Cannot access class or instance attributes (unless passed explicitly).
  • Used for utility/helper functions related to the class.
  • Is called on the actual class record.
class MathHelper:
    @staticmethod
    def add(a, b):
        return a + b

print(MathHelper.add(5, 7))
# 12

Dunder methods:

  • Special methods denoted with __ __ either side.
  • Has special purposes, such as the __init__ function.
  • See section below for more details.



Encapsulation (Attribute Access)

There is no true private attribute enforcement like in other languages, Instead a naming convention using _ and __ is used.


Public (no underscore):

  • Normal attributes are fully accessible.
class Demo:
    def __init__(self):
        self.value = 42

obj = Demo()
print(obj.value)
# Works fine


Protected (_single):

  • Just a convention: “for internal use, but you can access it if you really want.”
class Demo:
    def __init__(self):
        self._internal = "protected-ish"

obj = Demo()
print(obj._internal)
# Works, but convention says "don’t"

Private (__double):

  • Triggers name mangling: Python renames it to _ClassName__attr.
  • Makes accidental access harder, but still accessible if you know the mangled name.
class Demo:
    def __init__(self):
        self.__secret = "hidden"

obj = Demo()

# print(obj.__secret)
# AttributeError

print(obj._Demo__secret)
# Works as its manually using the mangled name, but not recommended




Inheritance

Inheritance is supported in Python classes, allowing you to use inherited attributes and methods and override methods.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "Woof!"

dog = Dog("Rex")

print(dog.name)
# Inherited from Animal

print(dog.speak())
# Overridden in Dog

The super() function

  • Calls methods of the parent class.
  • Commonly used in __init__ to ensure parent initialisation runs, see highlighted row in the example below where it calls the parents constructor first to initialise the common attributes and then sets the class specific attributes.
  • Avoids hardcoding the parent class name → important in multiple inheritance.
class Animal:
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    def __init__(self, name, breed):
        # Call parent constructor
        super().__init__(name)
        self.breed = breed

dog = Dog("Rex", "Labrador")

print(dog.name)
# from Animal

print(dog.breed)
# from Dog

Example: Calling parent methods:

class Animal:
    def speak(self):
        return "Some sound"

class Cat(Animal):
    def speak(self):
        # Use parent method, then extend
        base_sound = super().speak()
        return base_sound + " Meow!"

cat = Cat()

print(cat.speak())
# "Some sound Meow!"

Multiple Inheritance and super()

Python supports multiple inheritance. super() follows the MRO (Method Resolution Order).

class A:
    def greet(self):
        print("Hello from A")

class B(A):
    def greet(self):
        print("Hello from B")
        super().greet()

class C(A):
    def greet(self):
        print("Hello from C")
        super().greet()

class D(B, C):
    def greet(self):
        print("Hello from D")
        super().greet()

d = D()
d.greet()

# Output:
# Hello from D
# Hello from B
# Hello from C
# Hello from A




Dunder methods

Python has a lot of dunder (double underscore) methods, also called magic methods. They let your objects integrate seamlessly with Python’s built-in syntax and functions.

  • Dunder methods = special hooks that let your class behave like built-in types.
  • This allows you to be able to dfine how you class works with the len method, or how it will print when doing an f sting etc.
  • Categories:
    • Initialisation (__init__, __str__, __repr__)
    • Attribute access (__getattr__, __setattr__)
    • Callable/context (__call__, __enter__, __exit__)
    • Comparisons (__eq__, __lt__, etc.)
    • Containers (__len__, __getitem__, etc.)
    • Operators (arithmetic, bitwise, in-place, reflected)
    • Misc (__bool__, __index__, async methods)

Examples:

class MyCollection:
    def __init__(self, items):
        self.items = list(items)

    # String representation
    def __str__(self):
        return f"MyCollection({self.items})"

    def __repr__(self):
        return f"MyCollection({self.items!r})"

    # Length
    def __len__(self):
        return len(self.items)

    # Indexing / slicing
    def __getitem__(self, index):
        return self.items[index]

    def __setitem__(self, index, value):
        self.items[index] = value

    def __delitem__(self, index):
        del self.items[index]

    # Iteration
    def __iter__(self):
        return iter(self.items)

    # Membership test
    def __contains__(self, item):
        return item in self.items

    # Addition (combine two collections)
    def __add__(self, other):
        return MyCollection(self.items + other.items)

    # Equality
    def __eq__(self, other):
        return self.items == other.items

    # Boolean
    def __bool__(self):
        return bool(self.items)

    # Callable
    def __call__(self):
        print("This collection has", len(self), "items!")

# ------------------------------------
# Usage
# ------------------------------------

col1 = MyCollection([1, 2, 3])
col2 = MyCollection([4, 5])

# __str__
print("String:", col1)

# __repr__
print("Representation:", repr(col1))

# __len__
print("Length:", len(col1))

# __getitem__
print("Indexing:", col1[1])

# __setitem__
col1[1] = 20
print("After setitem:", col1)

# __delitem__
del col1[0]
print("After delitem:", col1)

# __iter__
print("Iteration:", [x for x in col1])

# __contains__
print("Membership 20 in col1?", 20 in col1)

# __add__
col3 = col1 + col2
print("Addition:", col3)

# __eq__
print("Equality col1==col3?", col1 == col3)

# __bool__
print("Boolean col1?", bool(col1))

# __call__
col1()




Full demonstration of classes

# Base class
class Animal:
    # Class attribute
    species = "Generic Animal"

    def __init__(self, name, age):
        # Instance attributes
        self.name = name
        self.age = age

    def speak(self):
        # Instance method
        return "Some generic sound"

    @classmethod
    def class_info(cls):
        # Class method
        return f"This is the {cls.__name__} class."

    @staticmethod
    def kingdom():
        # Static method
        return "Animalia"

    def __str__(self):
        # String representation
        return f"{self.name} ({self.age} yrs)"

# Derived class (inheritance)
class Dog(Animal):
    species = "Canis familiaris"

    def speak(self):
        # Override parent method
        return f"{self.name} says Woof!"

# Another derived class
class Cat(Animal):
    species = "Felis catus"

    def speak(self):
        # Override parent method
        return f"{self.name} says Meow!"

# Custom class with operator overloading
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __add__(self, other):
        # Overload +
        return Point(self.x + other.x, self.y + other.y)

    def __str__(self):
        return f"({self.x}, {self.y})"

# ------------------------------------
# Usage
# ------------------------------------

# Create objects
dog = Dog("Buddy", 5)
cat = Cat("Lucy", 3)

print("Dog:", dog)
print("Cat:", cat)
print("Dog speaks:", dog.speak())
print("Cat speaks:", cat.speak())

# Class method and static method
print("Class method:", Dog.class_info())
print("Static method:", Animal.kingdom())

# Inheritance in action
animals = [dog, cat, Animal("Generic", 1)]
for a in animals:
    print(f"{a.name} is a {a.species} and says: {a.speak()}")

# Operator overloading
p1, p2 = Point(1, 2), Point(3, 4)
print("Points:", p1, "+", p2, "=", p1 + p2)

Products from our shop

Docker Cheat Sheet - Print at Home Designs

Docker Cheat Sheet - Print at Home Designs

Docker Cheat Sheet Mouse Mat

Docker Cheat Sheet Mouse Mat

Docker Cheat Sheet Travel Mug

Docker Cheat Sheet Travel Mug

Docker Cheat Sheet Mug

Docker Cheat Sheet Mug

Vim Cheat Sheet - Print at Home Designs

Vim Cheat Sheet - Print at Home Designs

Vim Cheat Sheet Mouse Mat

Vim Cheat Sheet Mouse Mat

Vim Cheat Sheet Travel Mug

Vim Cheat Sheet Travel Mug

Vim Cheat Sheet Mug

Vim Cheat Sheet Mug

SimpleSteps.guide branded Travel Mug

SimpleSteps.guide branded Travel Mug

Developer Excuse Javascript - Travel Mug

Developer Excuse Javascript - Travel Mug

Developer Excuse Javascript Embroidered T-Shirt - Dark

Developer Excuse Javascript Embroidered T-Shirt - Dark

Developer Excuse Javascript Embroidered T-Shirt - Light

Developer Excuse Javascript Embroidered T-Shirt - Light

Developer Excuse Javascript Mug - White

Developer Excuse Javascript Mug - White

Developer Excuse Javascript Mug - Black

Developer Excuse Javascript Mug - Black

SimpleSteps.guide branded stainless steel water bottle

SimpleSteps.guide branded stainless steel water bottle

Developer Excuse Javascript Hoodie - Light

Developer Excuse Javascript Hoodie - Light

Developer Excuse Javascript Hoodie - Dark

Developer Excuse Javascript Hoodie - Dark

© 2025 SimpleSteps.guide
AboutFAQPoliciesContact