Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Foundations of Object-Oriented Programming in Python

Tech May 14 1

Defining Classes and Instances

A class acts as a blueprint for creating specific objects. Variables defined directly within the class body are referred to as member variables.

class UserProfile:
    username = None
    role = None
    active_status = None

Instantiating the class generates an independent object:

user_instance = UserProfile()

Attributes can be modified individually after creation:

user_instance.username = "admin"
user_instance.role = "manager"

Functions defined inside a class are known as member methods. The first parameter self is mandatory when defining them, representing the instance itself.

def display_info(self, param_one, param_two):
    pass

When calling these methods on an instance, passing the object as an argument for self is typically handled automatically by the interpreter.

Initializing Objects

Special constructors allow customization immediately upon object generation. The __init__ method executes automatically during instantiation, receiving parameters passed from the constructor call.

class ConfigHandler:
    def __init__(self, base_path, config_file):
        self.base_path = base_path
        self.config_file = config_file

The keyword self must also appear here. Accessing member variables inside methods requires referencing self.

Special Methods

Python supports special methods (often called magic methods) to define operator overloading and object behavior.

String Representation

The __str__ method controls how an object is displayed when converted to a string format.

Comparison Operators

Methods like __lt__ (less than), __le__ (less than or equal), and __eq__ (equal) customize comparison logic between instances.

Callability

Defining __call__ makes an instance behave like a function. When called, it executes the code within that method.

class ScriptRunner:
    pass

runner = ScriptRunner()
print(f"is callable: {callable(runner)}")  # Output: False

By adding __call__:

class ScriptRunner:
    def __call__(self):
        print("Executing task script")

runner = ScriptRunner()
print(f"is callable: {callable(runner)}")  # Output: True
runner()

Encapsulation Strategies

Encapsulation restricts access to certain components to maintain data integrity.

  • Protected Members: Single underscore prefix (e.g., _var) suggests internal use but remains accessible.
  • Private Members: Double underscore prefix (e.g., __var) triggers name mangling. Python renames these to _ClassName__var, preventing direct external access even from subclasses.

Inheritance Mechanisms

Subclasses derive from parent classes to reuse and extend functionality.

class NetworkDevice:
    device_id = None
    manufacturer = "Generic"

    def transmit_data(self):
        print("Sending via standard protocol")

class Router(NetworkDevice):
    manufacturer = "NetBrand"

    def transmit_data(self):
        # Direct parent access
        print(f"Using parent brand: {NetworkDevice.manufacturer}")
        NetworkDevice.transmit_data(self)

        # Using super()
        print(f"Using parent brand: {super().manufacturer}")
        super().transmit_data()

Multiple inheritance is supported. If parents share attributes, priority follows the order of declaration (leftmost takes precedence). Overriding occurs when a subclass defines a method already present in the parent.

To invoke overridden parent members:

  1. Direct Access: Use ParentClass.method_name(self).
  2. Super Function: Use super().method_name().

Type Hints

Static type checking can be enhanced using annotations to assist IDEs with inference and documentation.

count: int = 10
flags: bool = True
ids: list[int] = [1, 2, 3]
pairs: dict[str, int] = {"key": 1}

Complex container types require specifying element types:

my_tuple: tuple[str, int, bool] = ("abc", 123, True)

Function annotations include parameter types and return values:

def process_data(item: list[int]) -> str:
    return f"Processed {len(item)} items"

For flexible typing, Union types allow multiple possibilities:

from typing import Union
value: list[Union[str, int]] = [1, "hello"]

def handle_input(data: Union[int, str]) -> Union[int, str]:
    pass

Polymorphism

Polymorphism allows interface-based design where specific implementations vary while sharing a common signature. Base classes establish abstract standards without implementation logic.

class SmartAppliance:
    def activate_mode(self):
        pass

class Thermostat(SmartAppliance):
    def activate_mode(self):
        print("Heating active")

class LightSwitch(SmartAppliance):
    def activate_mode(self):
        print("Light turned on")

Abstract parent classes define contracts, while concrete subclasses provide specific logic, enabling flexible system design.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.