Projects

Top 10 Design Patterns for Python Classes

Python classes serve as the cornerstone of object-oriented programming (OOP), allowing programmers to define the blueprint for creating objects. A class encapsulates both data (attributes) and functionalities (methods) that operate on the data. Defined using the class keyword, a class contains an optional constructor method called __init__ for initializing attributes, and can also have other methods for manipulating these attributes or performing tasks. Following are the coding patterns for python classes.

  1. Singleton Pattern for Python Classes:
    The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance.
class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

# Usage
instance1 = Singleton()
instance2 = Singleton()
print(instance1 is instance2)  # Output: True
  1. Factory Pattern for Python Classes::
    The Factory pattern creates objects without specifying the exact class of object that will be created.
class Dog:
    def speak(self):
        return "Woof!"

class Cat:
    def speak(self):
        return "Meow!"

class AnimalFactory:
    def create_animal(self, animal_type):
        if animal_type == "dog":
            return Dog()
        elif animal_type == "cat":
            return Cat()

# Usage
factory = AnimalFactory()
animal = factory.create_animal("dog")
print(animal.speak())  # Output: Woof!
  1. Observer Pattern:
    The Observer pattern defines a dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
class Observer:
    def update(self, message):
        pass

class ConcreteObserver(Observer):
    def update(self, message):
        print("Received message:", message)

class Subject:
    _observers = []

    def attach(self, observer):
        self._observers.append(observer)

    def notify_observers(self, message):
        for observer in self._observers:
            observer.update(message)

# Usage
subject = Subject()
observer1 = ConcreteObserver()
observer2 = ConcreteObserver()

subject.attach(observer1)
subject.attach(observer2)

subject.notify_observers("Hello, observers!")
  1. Decorator Pattern:
    The Decorator pattern allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.
class Coffee:
    def cost(self):
        return 5

class MilkDecorator:
    def __init__(self, coffee):
        self._coffee = coffee

    def cost(self):
        return self._coffee.cost() + 2

# Usage
simple_coffee = Coffee()
milk_coffee = MilkDecorator(simple_coffee)
print(milk_coffee.cost())  # Output: 7
  1. Strategy Pattern:
    The Strategy pattern defines a family of algorithms, encapsulates each algorithm, and makes them interchangeable. This allows a client to choose an algorithm from a family of algorithms at runtime.
class Strategy:
    def execute(self, a, b):
        pass

class Add(Strategy):
    def execute(self, a, b):
        return a + b

class Subtract(Strategy):
    def execute(self, a, b):
        return a - b

class Context:
    def __init__(self, strategy):
        self._strategy = strategy

    def execute_strategy(self, a, b):
        return self._strategy.execute(a, b)

# Usage
add_strategy = Add()
context = Context(add_strategy)
result = context.execute_strategy(5, 3)
print(result)  # Output: 8
  1. Template Method Pattern:
    The Template Method pattern defines the structure of an algorithm in a base class but lets subclasses override specific steps of the algorithm without changing its structure.
class Template:
    def template_method(self):
        self.step_one()
        self.step_two()

    def step_one(self):
        pass

    def step_two(self):
        pass

class ConcreteTemplate(Template):
    def step_one(self):
        print("Step one")

    def step_two(self):
        print("Step two")

# Usage
template = ConcreteTemplate()
template.template_method()
  1. Iterator Pattern:
    The Iterator pattern provides a way to access the elements of a collection without exposing its underlying representation.
class Iterator:
    def __init__(self, collection):
        self._collection = collection
        self._index = 0

    def __next__(self):
        if self._index < len(self._collection):
            item = self._collection[self._index]
            self._index += 1
            return item
        raise StopIteration

class Collection:
    def __init__(self):
        self._items = []

    def add_item(self, item):
        self._items.append(item)

    def __iter__(self):
        return Iterator(self._items)

# Usage
collection = Collection()
collection.add_item("Item 1")
collection.add_item("Item 2")

for item in collection:
    print(item)
  1. Command Pattern:
    The Command pattern encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing of requests, and logging of the requests.
class Command:
    def execute(self):
        pass

class Light:
    def turn_on(self):
        print("Light is on")

    def turn_off(self):
        print("Light is off")

class LightOnCommand(Command):
    def __init__(self, light):
        self._light = light

    def execute(self):
        self._light.turn_on()

class LightOffCommand(Command):
    def __init__(self, light):
        self._light = light

    def execute(self):
        self._light.turn_off()

class RemoteControl:
    def __init__(self):
        self._command = None

    def set_command(self, command):
        self._command = command

    def press_button(self):
        self._command.execute()

# Usage
light = Light()
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

remote = RemoteControl()
remote.set_command(light_on)
remote.press_button()  # Output: Light is on

remote.set_command(light_off)
remote.press_button()  # Output: Light is off
  1. Adapter Pattern:
    The Adapter pattern allows objects with incompatible interfaces to collaborate.
class EuropeanSocket:
    def voltage(self):
        return 230

class AmericanSocket:
    def voltage(self):
        return 120

class AmericanToEuropeanAdapter(EuropeanSocket):
    def __init__(self, american_socket):
        self._american_socket = american_socket

    def voltage(self):
        return self._american_socket.voltage() * 2

# Usage
american_socket = AmericanSocket()
adapter = AmericanToEuropeanAdapter(american_socket)
print(adapter.voltage())  # Output: 240
  1. Facade Pattern:
    The Facade pattern provides a unified interface to a set of interfaces in a subsystem. It defines a higher-level interface that makes the subsystem easier to use.
class SubsystemA:
    def operation_a(self):
        return "Subsystem A operation"

class SubsystemB:
    def operation_b(self):
        return "Subsystem B operation"

class Facade:
    def __init__(self):
        self._subsystem_a = SubsystemA()
        self._subsystem_b = SubsystemB()
    
    def operation(self):
        result = []
        result.append

FAQ on Python Classes

1. What is a Class in Python?

A class in Python is a blueprint for creating objects. Classes encapsulate data for the object and methods to manipulate that data. The class is defined using the class keyword followed by the name of the class and a colon.

Example:

Python
class MyClass:
    x = 5

2. How Do I Create an Object of a Class?

Once a class is defined, you can create an object of that class using the class name followed by parentheses.

Example:

Python
my_object = MyClass()
print(my_object.x)  # Output will be 5

3. What is the __init__ Method?

The __init__ method is a special method in Python classes, also known as the “constructor.” It’s automatically called when you create a new object of a class. The method is useful for any initialization you want to do with your object.

Example:

Python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

To create an object of this class, you would do:

person1 = Person("John", 30)

4. What is Inheritance in Python?

Inheritance allows a class to inherit attributes and methods from another class. The class inheriting is known as the “subclass,” and the class being inherited from is known as the “superclass.”

Example:

Python
class Student(Person):
    def __init__(self, name, age, grade):
        super().__init__(name, age)
        self.grade = grade

In this example, Student is a subclass of Person and inherits its __init__ method.

5. What is the Difference Between Class Variables and Instance Variables?

  • Class Variables: These are variables that are shared among all instances (objects) of a class. They are defined within the class but outside any methods. Example:
Python
  class MyClass:
      class_var = "I'm a class variable"
  • Instance Variables: These variables are unique to each instance of a class. They are defined within methods and are attached to the self keyword. Example:
Python
  class MyClass:
      def __init__(self, x):
          self.instance_var = x