Python Beyond the Basics

"Python embraces many functional programming concepts to enable cleaner and more declarative code. While not a purely functional language, it incorporates tools that treat functions as first-class citizens and focus on transforming data."- Gemini 2025

Functional Programming

Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data.

Key Principles:
  • Pure Functions: Same input always produces same output
  • Immutability: Data doesn't change after creation
  • Higher-Order Functions: Functions that take other functions as arguments
Why Functional Programming?
  • More predictable code
  • Easier testing
  • Better parallelization
  • Fewer bugs
Example: Pure vs Impure Functions
# Pure function - same input, same output
def add_pure(a, b):
    return a + b

# Impure function - depends on external state
counter = 0
def add_impure(a, b):
    global counter
    counter += 1
    return a + b + counter

# Pure function usage
result1 = add_pure(2, 3)  # Always returns 5
result2 = add_pure(2, 3)  # Always returns 5
print(f"Pure: {result1}, {result2}")

# Impure function usage
result3 = add_impure(2, 3)  # Returns 6 (2+3+1)
result4 = add_impure(2, 3)  # Returns 7 (2+3+2)
print(f"Impure: {result3}, {result4}")
Output:
Pure: 5, 5
Impure: 6, 7
1. Lambda Expressions

Lambda expressions are small anonymous functions that can have any number of arguments but can only have one expression.

Syntax and Examples
# Basic syntax: lambda arguments: expression

# Simple lambda
square = lambda x: x ** 2
print(f"Square of 5: {square(5)}")

# Lambda with multiple arguments
multiply = lambda x, y: x * y
print(f"3 * 4 = {multiply(3, 4)}")

# Lambda with conditional
max_value = lambda a, b: a if a > b else b
print(f"Max of 10 and 15: {max_value(10, 15)}")

# Using lambda in sorting
students = [('Alice', 85), ('Bob', 90), ('Charlie', 78)]
students.sort(key=lambda student: student[1])  # Sort by grade
print(f"Sorted by grade: {students}")

# Lambda in list operations
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(f"Doubled numbers: {doubled}")
Output:
Square of 5: 25
3 * 4 = 12
Max of 10 and 15: 15
Sorted by grade: [('Charlie', 78), ('Alice', 85), ('Bob', 90)]
Doubled numbers: [2, 4, 6, 8, 10]
2. List Comprehension

List comprehension provides a concise way to create lists based on existing lists or other iterables.

Syntax and Examples
# Basic syntax: [expression for item in iterable if condition]

# Basic list comprehension
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
squares = [x**2 for x in numbers]
print(f"Squares: {squares}")

# With condition
evens = [x for x in numbers if x % 2 == 0]
print(f"Even numbers: {evens}")

# More complex expression
formatted = [f"Number: {x}" for x in numbers if x > 5]
print(f"Formatted: {formatted}")

# Nested list comprehension
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [item for row in matrix for item in row]
print(f"Flattened matrix: {flattened}")

# Dictionary comprehension
word_lengths = {word: len(word) for word in ['python', 'java', 'javascript']}
print(f"Word lengths: {word_lengths}")

# Set comprehension
unique_squares = {x**2 for x in [1, 2, 2, 3, 3, 4]}
print(f"Unique squares: {unique_squares}")
Output:
Squares: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Even numbers: [2, 4, 6, 8, 10]
Formatted: ['Number: 6', 'Number: 7', 'Number: 8', 'Number: 9', 'Number: 10']
Flattened matrix: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Word lengths: {'python': 6, 'java': 4, 'javascript': 10}
Unique squares: {1, 4, 9, 16}
3. Map, Filter, and Reduce

These are built-in functions that allow functional programming approaches to data processing.

Map, Filter, and Reduce Examples
from functools import reduce

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# MAP: Apply function to all elements
# map(function, iterable)
squared = list(map(lambda x: x**2, numbers))
print(f"Map - Squared: {squared}")

# Convert to strings
str_numbers = list(map(str, numbers))
print(f"Map - Strings: {str_numbers}")

# FILTER: Filter elements based on condition
# filter(function, iterable)
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Filter - Evens: {evens}")

# Filter strings by length
words = ['cat', 'elephant', 'dog', 'hippopotamus']
long_words = list(filter(lambda w: len(w) > 5, words))
print(f"Filter - Long words: {long_words}")

# REDUCE: Reduce iterable to single value
# reduce(function, iterable, initial_value)
total = reduce(lambda x, y: x + y, numbers)
print(f"Reduce - Sum: {total}")

max_value = reduce(lambda x, y: x if x > y else y, numbers)
print(f"Reduce - Max: {max_value}")

# Combining map, filter, and reduce
result = reduce(
    lambda x, y: x + y,
    map(lambda x: x**2,
        filter(lambda x: x % 2 == 0, numbers)
    )
)
print(f"Combined - Sum of squares of evens: {result}")
Output:
Map - Squared: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Map - Strings: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
Filter - Evens: [2, 4, 6, 8, 10]
Filter - Long words: ['elephant', 'hippopotamus']
Reduce - Sum: 55
Reduce - Max: 10
Combined - Sum of squares of evens: 220
4. Iterators & Generators
Iterators

Objects that implement the iterator protocol (__iter__ and __next__ methods).

Generators

Functions that return an iterator using the yield keyword. Memory efficient!

Iterator and Generator Examples
# Custom Iterator Class
class CountUp:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.end:
            num = self.current
            self.current += 1
            return num
        else:
            raise StopIteration

# Using the iterator
counter = CountUp(1, 5)
for num in counter:
    print(f"Iterator: {num}")

# Generator Function
def fibonacci_generator(n):
    a, b = 0, 1
    count = 0
    while count < n:
        yield a
        a, b = b, a + b
        count += 1

# Using the generator
print("Generator - Fibonacci sequence:")
for fib in fibonacci_generator(8):
    print(fib, end=' ')
print()

# Generator Expression
squares_gen = (x**2 for x in range(5))
print(f"Generator expression: {list(squares_gen)}")

# Generator with infinite sequence
def infinite_counter():
    count = 0
    while True:
        yield count
        count += 1

# Take only first 5 values
counter_gen = infinite_counter()
first_five = [next(counter_gen) for _ in range(5)]
print(f"Infinite generator (first 5): {first_five}")

# Memory efficiency demonstration
import sys
list_comp = [x**2 for x in range(1000)]
gen_expr = (x**2 for x in range(1000))
print(f"List comprehension size: {sys.getsizeof(list_comp)} bytes")
print(f"Generator expression size: {sys.getsizeof(gen_expr)} bytes")
Output:
Iterator: 1
Iterator: 2
Iterator: 3
Iterator: 4
Generator - Fibonacci sequence:
0 1 1 2 3 5 8 13
Generator expression: [0, 1, 4, 9, 16]
Infinite generator (first 5): [0, 1, 2, 3, 4]
List comprehension size: 8856 bytes
Generator expression size: 104 bytes
5. Collections

The collections module provides specialized container datatypes providing alternatives to Python's general purpose built-in containers.

Collections Module Examples
from collections import Counter, defaultdict, namedtuple, deque, OrderedDict

# COUNTER: Count occurrences
text = "hello world"
letter_count = Counter(text)
print(f"Counter: {letter_count}")
print(f"Most common: {letter_count.most_common(3)}")

# DEFAULTDICT: Dictionary with default values
dd = defaultdict(list)
words = ['apple', 'banana', 'apple', 'cherry', 'banana']
for word in words:
    dd[word[0]].append(word)  # Group by first letter
print(f"DefaultDict: {dict(dd)}")

# NAMEDTUPLE: Immutable objects with named fields
Person = namedtuple('Person', ['name', 'age', 'city'])
john = Person('John', 30, 'New York')
print(f"NamedTuple: {john}")
print(f"Name: {john.name}, Age: {john.age}")

# DEQUE: Double-ended queue (efficient append/pop from both ends)
dq = deque(['a', 'b', 'c'])
dq.appendleft('z')  # Add to left
dq.append('d')      # Add to right
print(f"Deque: {dq}")
print(f"Pop left: {dq.popleft()}, Pop right: {dq.pop()}")
print(f"Final deque: {dq}")

# ORDEREDDICT: Dictionary that remembers insertion order (Python 3.7+ dicts are ordered)
od = OrderedDict()
od['first'] = 1
od['second'] = 2
od['third'] = 3
print(f"OrderedDict: {od}")

# Practical example: Word frequency analysis
def analyze_text(text):
    words = text.lower().split()
    word_freq = Counter(words)
    word_groups = defaultdict(list)

    for word, freq in word_freq.items():
        word_groups[freq].append(word)

    return word_freq, dict(word_groups)

sample_text = "the quick brown fox jumps over the lazy dog the fox is quick"
freq, groups = analyze_text(sample_text)
print(f"Word frequency: {freq}")
print(f"Grouped by frequency: {groups}")
Output:
Counter: Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
Most common: [('l', 3), ('o', 2), ('h', 1)]
DefaultDict: {'a': ['apple', 'apple'], 'b': ['banana', 'banana'], 'c': ['cherry']}
NamedTuple: Person(name='John', age=30, city='New York')
Name: John, Age: 30
Deque: deque(['z', 'a', 'b', 'c', 'd'])
Pop left: z, Pop right: d
Final deque: deque(['a', 'b', 'c'])
GUI with PyQt5

PyQt5 is more robust across different environments and systems.

Note: This simple GUI app creates a window with a label and button, and when clicked, the button changes the label text.
Simple GUI
# pip install pyqt5

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton

class SimpleGUI(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Simple GUI')
        self.setGeometry(300, 300, 250, 150)

        # Create layout
        layout = QVBoxLayout()

        # Create label
        self.label = QLabel('Hello, World!')
        layout.addWidget(self.label)

        # Create button
        button = QPushButton('Click Me!')
        button.clicked.connect(self.button_clicked)
        layout.addWidget(button)

        self.setLayout(layout)

    def button_clicked(self):
        self.label.setText('Button was clicked!')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = SimpleGUI()
    window.show()
    sys.exit(app.exec_())
→ This page was created with help from Claude AI.