Core Python Concepts: Classes, Modules, Data Structures, and Expressions

Class Definition

In Python, a class is defined using the class keyword followed by the class name and a colon. The body of the class is indented and may contain attributes and methods.

class Vehicle:
    category = "Land"

    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def describe(self):
        print(f"{self.brand} {self.model}")

# Usage
car = Vehicle("Toyota", "Camry")
car.describe()  # Output: Toyota Camry

The __init__ method initializes instance attributes. The self parameter refers to the current instance and is used to access its attributes and methods.

Inheritance

A subclass inherits attributes and methods from a parent class. It can override or extend functionality.

class Car(Vehicle):
    def __init__(self, brand, model, doors):
        super().__init__(brand, model)
        self.doors = doors

    def describe(self):
        super().describe()
        print(f"Doors: {self.doors}")

toyota = Car("Toyota", "Camry", 4)
toyota.describe()
# Output:
# Toyota Camry
# Doors: 4

Python supports multiple inheritence, where a class inherits from more than one parent. Method resolution follows the Method Resolution Order (MRO).

Polymorphism

Polymorphism allows objects of different classes to be treated through a common interface. This is achieved via method overriding or duck typing.

class Bird:
    def speak(self):
        print("Chirp!")

class Robot:
    def speak(self):
        print("Beep!")

def announce(entity):
    entity.speak()

announce(Bird())   # Chirp!
announce(Robot())  # Beep!

Modules and Packages

A module is a .py file containing related code. A package is a directory containing an __init__.py file (optional in Python ≥3.3 but recommended) and one or more modules.

The __init__.py file can:

  • Mark the directory as a package.
  • Initialize package-level data.
  • Control what is imported with from package import * via the __all__ list.
# mypkg/__init__.py
__all__ = ["utils", "core"]

Built-in Data Structures

Lists are ordered, mutable sequences.

items = [10, 20, 30]
items.append(40)          # Add element
items.extend([50, 60])    # Add multiple elements
print(items[1:4])         # Slicing: [20, 30, 40]

Sets store unique, unordered elements.

unique_nums = {1, 2, 3}
unique_nums.add(4)
print(3 in unique_nums)  # True
print(unique_nums | {5}) # Union: {1, 2, 3, 4, 5}

Dictionaries map keys to values.

config = {"host": "localhost", "port": 8080}
config["debug"] = True
for k, v in config.items():
    print(f"{k}: {v}")

Dictionary unpacking uses ** to pass key-value pairs as keyword arguments:

def connect(host, port):
    return f"Connecting to {host}:{port}"

params = {"host": "example.com", "port": 443}
print(connect(**params))  # Connecting to example.com:443

List Comprehensions and Generator Expresions

List comprehensions provide a concise way to create lists:

squares = [x**2 for x in range(5) if x % 2 == 0]  # [0, 4, 16]
word_lengths = {w: len(w) for w in ["cat", "dog", "elephant"] if len(w) > 3}
# {'elephant': 8}

Generator expressions use parentheses and produce items lazily:

evens = (x for x in range(10) if x % 2 == 0)
print(list(evens))  # [0, 2, 4, 6, 8]

Functional Tools

The map() function applies a function to every item in an iterable:

lengths = list(map(len, ["apple", "fig", "banana"]))  # [5, 3, 6]

Lambda functions are anonymous, inline functions:

double = lambda x: x * 2
print(double(5))  # 10

String Formatting

f-strings (formatted string literals) embed expressions inside strings:

name = "Eve"
age = 28
msg = f"{name} is {age} years old."  # Eve is 28 years old.

The str.format() method offers an alternative:

msg = "{} is {} years old.".format(name, age)

Dynamic Attribute Access

getattr() retrieves an object’s attribute dynamically:

class Config:
    debug = True

cfg = Config()
mode = getattr(cfg, "debug", False)  # True
verbosity = getattr(cfg, "log_level", "INFO")  # 'INFO'

Logging

The logging module provides a flexible logging system:

import logging

logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
logger = logging.getLogger(__name__)
logger.info("Application started")

Randomness and Reproducibility

Setting a seed ensures reproducible random sequences:

import random
random.seed(123)
print(random.randint(1, 10))  # Always 1

Similarly, NumPy uses np.random.seed().

Slicing

Slicing extracts subsequences with [start:stop:step]:

data = [0, 1, 2, 3, 4, 5]
print(data[1:5:2])   # [1, 3]
print(data[::-1])    # [5, 4, 3, 2, 1, 0]

Ganerators and yield

Functions with yield return generators that produce values on demand:

def count_up_to(n):
    i = 1
    while i <= n:
        yield i
        i += 1

for num in count_up_to(3):
    print(num)  # 1, 2, 3

Tags: python Classes Inheritance Modules data-structures

Posted on Wed, 19 Aug 2026 16:29:49 +0000 by ouch!