๐Ÿƒ Python: Core Concepts

Types, comprehensions, OOP, async, decorators & stdlib essentials

Card 0 of 20 flipped
types

What are Python's core data types?

Immutable: int, float, str, bool, tuple, frozenset. Mutable: list, dict, set. None is a singleton null type. Everything in Python is an object.

x = 42          # int
pi = 3.14       # float
name = 'Alice'  # str
flag = True     # bool
nums = [1,2,3]  # list (mutable)
coords = (1,2)  # tuple (immutable)
uniq = {1,2,3}  # set
data = {'a': 1} # dict
nothing = None  # NoneType
#1
comprehensions

How do list comprehensions work?

Concise syntax for creating lists from iterables. Supports filtering with if, nested loops, and expressions. Faster than equivalent for loops.

# Basic
squares = [x**2 for x in range(10)]

# With filter
evens = [x for x in range(20) if x % 2 == 0]

# With transform + filter
names = [name.upper() for name in users if name.startswith('S')]

# Dict comprehension
word_len = {w: len(w) for w in words}

# Set comprehension
unique_lengths = {len(w) for w in words}
#2
functions

How do *args and **kwargs work?

*args collects positional arguments into a tuple. **kwargs collects keyword arguments into a dict. Use for flexible function signatures. * and ** also unpack in function calls.

def greet(*args, **kwargs):
    for name in args:
        print(f"Hi {name}")
    for key, val in kwargs.items():
        print(f"{key}: {val}")

greet('Alice', 'Alex', role='dev', lang='Python')

# Unpacking
nums = [1, 2, 3]
print(*nums)  # 1 2 3
config = {'host': 'localhost', 'port': 8080}
connect(**config)
#3
functions

How do decorators work in Python?

Decorators wrap functions to add behavior. They take a function, return a new function. Use @decorator syntax. Common uses: logging, timing, auth, caching.

import functools

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.time()-start:.2f}s")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return 'done'
#4
OOP

How does Python's class system work?

Classes define objects with __init__ constructor, self reference, methods, and class/static methods. Supports inheritance, multiple inheritance, and dunder methods for operator overloading.

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def greet(self):
        return f"Hi, I'm {self.name}"

    def __repr__(self):
        return f"User({self.name!r}, {self.email!r})"

    def __eq__(self, other):
        return self.email == other.email

class Admin(User):
    def __init__(self, name, email, level):
        super().__init__(name, email)
        self.level = level
#5
iterators

What are generators and how do they work?

Generators produce values lazily with yield. They're memory-efficient for large sequences. Generator expressions use () instead of []. They implement the iterator protocol.

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# Lazy โ€” only computes on demand
fib = fibonacci()
for _ in range(10):
    print(next(fib))

# Generator expression
sum_sq = sum(x**2 for x in range(1000000))
# Memory: O(1) vs list comprehension O(n)
#6
errors

How does exception handling work in Python?

try/except catches exceptions. else runs if no exception. finally always runs. Raise custom exceptions with raise. Create custom exceptions by inheriting Exception.

try:
    result = 10 / x
except ZeroDivisionError:
    print("Can't divide by zero")
except (TypeError, ValueError) as e:
    print(f"Bad input: {e}")
else:
    print(f"Result: {result}")  # no exception
finally:
    cleanup()  # always runs

# Custom exception
class ValidationError(Exception):
    def __init__(self, field, message):
        self.field = field
        super().__init__(message)
#7
patterns

What are context managers and the with statement?

Context managers handle setup/teardown automatically. with statement ensures cleanup even if exceptions occur. Built-in: files, locks, DB connections. Create with __enter__/__exit__ or @contextmanager.

# Built-in file context manager
with open('data.txt', 'r') as f:
    content = f.read()
# File auto-closed, even on exception

# Custom context manager
from contextlib import contextmanager

@contextmanager
def timer(label):
    start = time.time()
    yield  # code in 'with' block runs here
    print(f"{label}: {time.time()-start:.2f}s")

with timer('processing'):
    heavy_computation()
#8
data-structures

How do Python dictionaries work?

Hash map with O(1) average lookup. Keys must be hashable (immutable). Ordered since Python 3.7. Methods: get(), items(), keys(), values(), setdefault(), update().

d = {'name': 'Alice', 'role': 'dev'}

# Safe access with default
age = d.get('age', 0)  # 0 if missing

# Merge (Python 3.9+)
merged = d | {'age': 30, 'lang': 'Python'}

# Iteration
for key, value in d.items():
    print(f"{key}: {value}")

# defaultdict for counting
from collections import defaultdict
counts = defaultdict(int)
for word in words:
    counts[word] += 1
#9
strings

What are f-strings and string formatting?

f-strings (Python 3.6+) embed expressions in strings with {}. Support format specs, method calls, and expressions. The modern replacement for .format() and % formatting.

name = 'Alice'
age = 30

# f-string (preferred)
print(f"{name} is {age} years old")
print(f"Pi: {3.14159:.2f}")        # 3.14
print(f"{'hello':>20}")             # right-align
print(f"{1000000:,}")               # 1,000,000
print(f"{name!r}")                   # repr: 'Alice'

# Debug (Python 3.8+)
print(f"{name=}, {age=}")  # name='Alice', age=30
#10
typing

How do you use type hints in Python?

Type hints add optional type annotations. Not enforced at runtime โ€” use mypy for static checking. Improves IDE support, documentation, and code clarity.

from typing import Optional, Union

def greet(name: str, times: int = 1) -> str:
    return f"Hello {name}! " * times

def find_user(user_id: int) -> Optional[dict]:
    return db.get(user_id)  # might return None

# Modern syntax (Python 3.10+)
def process(data: str | int) -> list[str]:
    ...

# Generics
def first(items: list[T]) -> T:
    return items[0]
#11
gotchas

What is the difference between == and is?

== compares values (equality). is compares identity (same object in memory). Use is only for None, True, False checks. Small integers (-5 to 256) are cached so is may be misleading.

a = [1, 2, 3]
b = [1, 2, 3]

a == b  # True  (same value)
a is b  # False (different objects)

# Correct None check
if result is None:
    print("No result")

# NOT: if result == None (works but wrong style)

# Integer caching gotcha
x = 256
y = 256
x is y  # True (cached)
x = 257
y = 257
x is y  # May be False!
#12
io

How do you work with files in Python?

Use open() with context managers. Modes: r (read), w (write/truncate), a (append), x (create new), b (binary). pathlib is the modern way to handle paths.

from pathlib import Path

# Read
text = Path('data.txt').read_text()

# Write
Path('output.txt').write_text('hello')

# Line by line (memory efficient)
with open('big.txt') as f:
    for line in f:
        process(line.strip())

# JSON
import json
data = json.loads(Path('config.json').read_text())
Path('out.json').write_text(json.dumps(data, indent=2))
#13
OOP

How do dataclasses work?

dataclasses auto-generate __init__, __repr__, __eq__, and more. Clean alternative to regular classes for data containers. Use frozen=True for immutable instances.

from dataclasses import dataclass, field

@dataclass
class User:
    name: str
    email: str
    age: int = 0
    tags: list[str] = field(default_factory=list)

user = User('Alice', '[email protected]', 30)
print(user)  # User(name='Alice', email='[email protected]', age=30, tags=[])

@dataclass(frozen=True)  # immutable
class Point:
    x: float
    y: float
#14
functional

What are Python's built-in higher-order functions?

map() applies function to each item. filter() keeps items where function returns True. sorted() with key function. reduce() accumulates values. Lambda for inline functions.

nums = [1, 2, 3, 4, 5]

# map โ€” transform
squared = list(map(lambda x: x**2, nums))

# filter โ€” select
evens = list(filter(lambda x: x % 2 == 0, nums))

# sorted with key
users = sorted(users, key=lambda u: u.age, reverse=True)

# reduce โ€” accumulate
from functools import reduce
total = reduce(lambda a, b: a + b, nums)  # 15

# Prefer comprehensions over map/filter:
[x**2 for x in nums]  # cleaner than map
#15
modules

How does Python's import system work?

import loads modules. from...import gets specific names. Packages are directories with __init__.py. Use relative imports within packages. __all__ controls * imports.

import os
import json
from pathlib import Path
from collections import defaultdict, Counter
from typing import Optional

# Alias
import numpy as np
import pandas as pd

# Relative import (within package)
from . import utils
from ..models import User

# Conditional import
try:
    import ujson as json
except ImportError:
    import json
#16
concurrency

What is the Global Interpreter Lock (GIL)?

The GIL allows only one thread to execute Python bytecode at a time. Limits CPU-bound parallelism with threads. Workarounds: multiprocessing (separate processes), C extensions, or async for I/O-bound work.

# Threads โ€” good for I/O-bound (GIL released during I/O)
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=5) as pool:
    results = pool.map(fetch_url, urls)

# Processes โ€” good for CPU-bound (bypasses GIL)
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor() as pool:
    results = pool.map(compute_heavy, data)

# Async โ€” best for I/O-bound, single thread
async def main():
    results = await asyncio.gather(*tasks)
#17
concurrency

How does async/await work in Python?

async def defines a coroutine. await pauses until the result is ready. asyncio.run() starts the event loop. Use asyncio.gather() for concurrent I/O. Single-threaded concurrency.

import asyncio
import aiohttp

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            return await resp.text()

async def main():
    urls = ['https://api.example.com/1', '/2', '/3']
    results = await asyncio.gather(
        *[fetch(url) for url in urls]
    )
    return results

asyncio.run(main())
#18
syntax

What is the walrus operator (:=)?

Assignment expression (Python 3.8+) assigns and returns a value in one step. Useful in while loops, comprehensions, and if statements to avoid repeated computation.

# Without walrus
line = input()
while line != 'quit':
    process(line)
    line = input()

# With walrus
while (line := input()) != 'quit':
    process(line)

# In comprehension
results = [y for x in data if (y := expensive(x)) > threshold]

# In if statement
if (match := pattern.search(text)) is not None:
    print(match.group())
#19
stdlib

What are Python's key standard library modules?

os/pathlib (filesystem), json (serialization), datetime (time), re (regex), collections (data structures), itertools (iteration), functools (HOFs), logging, unittest, typing.

from pathlib import Path          # modern file paths
from collections import Counter, deque, defaultdict
from itertools import chain, groupby, product
from functools import lru_cache, partial, reduce
from datetime import datetime, timedelta
from typing import Optional, Union
import re, json, os, logging, unittest

# Caching
@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2: return n
    return fibonacci(n-1) + fibonacci(n-2)
#20