Python CSV Data Structures

This page collects runnable examples of core Python: loading CSV data into built-in containers, defining functions, lambdas, decorators and closures, object-oriented classes, iterators and generators, and concurrency. The sections at the end tie the examples together with diagrams, a comparison of the containers, and common mistakes.

Reading CSV Data into Different Data Structures

import csv

# Sample CSV data
with open('data.csv', 'r') as file:
    reader = csv.reader(file)
    # Skipping the header
    next(reader)
    # Reading into a list
    data_list = [row for row in reader]

with open('data.csv', 'r') as file:
    reader = csv.reader(file)
    next(reader)
    # Reading into a tuple
    data_tuple = tuple(reader)

with open('data.csv', 'r') as file:
    reader = csv.reader(file)
    next(reader)
    # Reading into a set
    data_set = {tuple(row) for row in reader}

with open('data.csv', 'r') as file:
    reader = csv.DictReader(file)
    # Reading into a dictionary
    data_dict = [row for row in reader]

print('List:', data_list)
print('Tuple:', data_tuple)
print('Set:', data_set)
print('Dictionary:', data_dict)
    

Explanation

This Python program demonstrates how to read data from a CSV file and load it into different data structures like Lists, Tuples, Sets, and Dictionaries.

Python Functions Example

Defining Functions

def greet(name, msg='Hello'):
    return {msg}, {name}!

print(greet('Alice'))  # Output: Hello, Alice!
print(greet('Bob', 'Hi'))  # Output: Hi, Bob!
    

Lambda Functions

# Inline anonymous function
square = lambda x: x * x
print(square(5))  # Output: 25
    

Decorators

def decorator_func(func):
    def wrapper():
        print('Before function call')
        func()
        print('After function call')
    return wrapper

@decorator_func
def say_hello():
    print('Hello!')

say_hello()
    

Closures and Higher-Order Functions

# Function that returns another function
def make_multiplier(n):
    def multiplier(x):
        return x * n
    return multiplier

times_two = make_multiplier(2)
print(times_two(5))  # Output: 10

# Passing a function as an argument
def apply_func(func, value):
    return func(value)

print(apply_func(square, 4))  # Output: 16
    

Python Object-Oriented Programming

Classes and Objects

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

    def bark(self):
        return 'Woof!'

my_dog = Dog('Buddy', 5)
print(my_dog.name)  # Output: Buddy
print(my_dog.bark())  # Output: Woof!
    

Inheritance and Polymorphism

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

class Cat(Animal):
    def speak(self):
        return 'Meow'

my_cat = Cat()
print(my_cat.speak())  # Output: Meow
    

Encapsulation

class Person:
    def __init__(self, name, age):
        self.__name = name  # Private attribute
        self.age = age

    def get_name(self):
        return self.__name

person = Person('Alice', 30)
print(person.get_name())  # Output: Alice
# print(person.__name)  # This would raise an AttributeError
    

Dunder Methods

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        return f'{self.title} by {self.author}'

book = Book('1984', 'George Orwell')
print(book)  # Output: 1984 by George Orwell
    

Python Iterators and Generators Example

Using Iterators and Generators

my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_set = {1, 2, 3, 4, 5}
my_dict = {'a': 1, 'b': 2, 'c': 3}

# Iterating over a list
for item in my_list:
    print(item)

# Iterating over a tuple
for item in my_tuple:
    print(item)

# Iterating over a set
for item in my_set:
    print(item)

# Iterating over a dictionary
for key, value in my_dict.items():
    print(key, value)

Using Generators

# Basic generator function
def my_generator():
    yield 1
    yield 2
    yield 3

for value in my_generator():
    print(value)

# Generator expression
gen_exp = (x for x in range(4))
for value in gen_exp:
    print(value)

Explanation

This Python program demonstrates how to iterate over different data structures i
(Lists, Tuples, Sets, and Dictionaries) and how to use generators.

Python Concurrency

Multithreading vs. Multiprocessing

# Multithreading: Multiple threads within the same process
import threading

def print_numbers():
    for i in range(5):
        print(i)

thread = threading.Thread(target=print_numbers)
thread.start()

# Multiprocessing: Multiple processes with separate memory spaces
import multiprocessing

def print_numbers_mp():
    for i in range(5):
        print(i)

process = multiprocessing.Process(target=print_numbers_mp)
process.start()
process.join()
    

Asyncio

# Asyncio: Writing asynchronous code with async and await
import asyncio

async def async_print_numbers():
    for i in range(5):
        print(i)
        await asyncio.sleep(1)

async def main():
    await async_print_numbers()

asyncio.run(main())
    

1. Choosing a Data Structure

Choosing a Container for CSV Rows csv.reader rows as lists list ordered, mutable tuple immutable, hashable set unique, O(1) lookup dict keyed access dataclass / namedtuple named fields, typed

The CSV example loads the same rows into four containers, and each answers different questions efficiently. A list keeps rows in file order and supports indexing and slicing. A tuple is a fixed record that can be hashed, so it can be stored in a set or used as a dictionary key. A set removes duplicates and answers membership questions in constant average time. A dictionary maps a key, such as an id column, to the row, giving direct access without scanning. For readable code, csv.DictReader, dataclasses, or typing.NamedTuple give each field a name rather than a position.

ContainerLookupOrderedMutableHashable
listO(n) by value, O(1) by indexYesYesNo
tupleO(n) by value, O(1) by indexYesNoYes, if elements are
setO(1) averageNoYesNo (frozenset is)
dictO(1) average by keyInsertion orderYesNo

2. How Objects Work

Attribute Lookup and Method Resolution instance.__dict__ instance attributes Class methods, class attrs Base classes in MRO order object default dunders AttributeError not found

When you read obj.attr, Python checks data descriptors on the class (such as properties), then the instance's own dictionary, then the class and its bases in method resolution order (MRO), which you can inspect with Type.__mro__. Methods are functions stored on the class; accessing them through an instance binds self automatically. Inheritance and polymorphism in the examples work because a subclass method earlier in the MRO shadows the base version, and super() continues the lookup from the next class in the MRO. Python encapsulation is by convention: a single leading underscore means internal, and a double underscore triggers name mangling to avoid accidental clashes in subclasses, not to enforce privacy.

The iterator and generator examples rely on the same protocol. Any object with an __iter__ method returning an object with __next__ can drive a for loop; a generator function creates such an object automatically and pauses at each yield, keeping its local variables alive between values. That makes generators the natural way to stream large CSV files row by row without loading them into memory, and to build processing pipelines where each stage pulls from the previous one. The concurrency example adds the last piece: threads for waiting on I/O, and processes or native extensions for heavy computation.

3. Worked Example: Everything Together

import csv, io, functools, time
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor

data = io.StringIO("id,name,dept,salary\n1,Ana,eng,120\n2,Bo,ops,90\n3,Cy,eng,110\n")

@dataclass(frozen=True)
class Employee:
    id: int
    name: str
    dept: str
    salary: int

def timed(fn):                                   # decorator
    @functools.wraps(fn)
    def wrapper(*args, **kw):
        t = time.perf_counter()
        try:
            return fn(*args, **kw)
        finally:
            print(f"{fn.__name__}: {time.perf_counter() - t:.6f}s")
    return wrapper

@timed
def load(fh):
    return [Employee(int(r["id"]), r["name"], r["dept"], int(r["salary"]))
            for r in csv.DictReader(fh)]

def by_dept(rows):                               # generator
    groups = {}
    for e in rows:
        groups.setdefault(e.dept, []).append(e)
    yield from groups.items()

staff = load(data)
for dept, people in by_dept(staff):
    print(dept, sum(p.salary for p in people))

with ThreadPoolExecutor(max_workers=4) as pool:   # I/O-bound work in threads
    print(list(pool.map(lambda e: e.name.upper(), staff)))

4. Pitfalls