PROGRAMMING COURSE
One complete Python course: the language itself, then the libraries and frameworks people actually use it for — NumPy, pandas, Django, databases, algorithms, and machine learning.
message = "Hello from Python"
print(message)COURSE CURRICULUM
Work through a section at a time, or jump straight to the concept you need.
Explain what the Python interpreter does with your source code, and judge which environments can actually run a .py file.
Install a modern Python interpreter, verify which one is running, and point your editor at that exact interpreter.
Save Python code in a .py file, run it from a terminal, and predict its output from top-to-bottom execution.
Use Python's interactive prompt to test ideas: read >>> and ... prompts, know why expressions echo but assignments don't, reuse _, and inspect objects live.
Read and write Python blocks correctly by using indentation, colons, and bracket continuation as the language's actual grammar.
Write comments that explain why code is the way it is, and docstrings that your program and tools can read back at runtime.
Read any Python traceback bottom-up: name the exception, map each frame to a call, and pinpoint the line and value that caused the failure.
Pick a target Python minor version, gate version-specific code with sys.version_info tuples, and write code that follows PEP 8 naming and layout.
Bind, rebind, unpack, and delete Python names with confidence, and predict the errors that come from reading a name that has no value.
Distinguish a name from the object it points at, use is/id() to detect aliasing, and predict when two names share the same state.
Tell int, float, and complex apart, predict which type an expression returns, and know why float results are approximate while int results are exact.
Write Python string literals correctly, reason about escapes and raw strings, and treat a string as an indexable sequence of code points.
Use True, False and None correctly: know which values Python treats as falsy, why bool is an int, and when to test with is None.
Convert between Python's str, int, float, and bool deliberately, predict truncation and parsing failures, and know when Python promotes types for you.
Use type() to get an object's exact class and isinstance() to test class membership that respects inheritance, and know when each is right.
Tell mutable objects from immutable ones, predict when a change is visible through other names, and copy or rebind deliberately.
Name constants and other identifiers the way PEP 8 expects, and explain why UPPER_CASE protects nothing at runtime.
Track how CPython decides an object is dead: reference counts drop to zero immediately, and the cycle collector cleans up what counting cannot.
Use Python's arithmetic operators confidently, and predict what / , // , % and divmod return for positive, negative, and float operands.
Bind names with =, unpack and swap with tuple targets, and predict when += mutates an object in place versus rebinding the name.
Compare values with ==, !=, <, <=, >, >= and write chains like 0 <= x < 10, knowing exactly how Python expands and evaluates them.
Predict exactly what and, or, and not return, and use short-circuit evaluation to guard risky expressions and supply fallback values.
Tell object identity from value equality with is, and test containment in strings, lists, dicts and sets with in / not in.
Use &, |, ^, ~, << and >> to read, set, clear and toggle individual bits of Python integers, and predict the result without guessing.
Predict how Python groups any unparenthesized expression by applying precedence first, then associativity, and confirm the reading with parentheses.
Create Python strings with single, double, and triple quotes, choose the delimiter that avoids escaping, and know when adjacent literals merge.
Address any character or substring in a Python string with index and slice expressions, including negative indices and steps.
Explain why a str can never be edited in place, predict the quadratic cost of += in a loop, and build strings with a list plus join instead.
Clean, normalize, inspect and pad strings using the core str methods: strip, case conversion, the is* tests, and zfill/ljust/rjust/center.
Locate substrings with in, find, rfind, index and count, and produce modified copies with replace, including limited and repeated passes.
Cut strings into lists with split(), rsplit(), partition() and splitlines(), and rebuild them with sep.join() without hitting type errors.
Use f-strings to embed live expressions in string literals and control precision, width, alignment, and grouping with format specs.
Fill templates stored in variables using str.format() and the % operator, with positional, keyword, dict, width and precision specs.
Read and write Python literals that contain backslashes, knowing which escapes exist, what r"..." changes, and how many characters result.
Convert between str and bytes deliberately with encode/decode, predict byte lengths, and diagnose UnicodeDecodeError and mojibake.
Write if statements that branch on any Python object, and predict which values count as false without converting them yourself.
Order and structure elif chains so that exactly one branch runs, using the fact that reaching an elif proves every condition above it was false.
Use else as the catch-all for an if, and nest if/else blocks so that indentation makes each else bind to the test you intended.
Use Python's `A if cond else B` expression to produce a value inline, and read its precedence and evaluation order correctly.
Use match/case to destructure sequences, mappings and objects in one step, with guards, or-patterns and wildcards instead of long comparison chains.
Use pass to keep a required block deliberately empty, and tell it apart from continue, return None, and ... in stubs and handlers.
Rewrite deeply nested if-blocks as early-exit guard clauses in Python functions and loops without changing behaviour.
Explain how a for loop drives the iterator protocol, and predict which objects can be looped over twice and which are spent after one pass.
Build exact numeric sequences with range(), reason about its half-open bounds and step direction, and know why it is a lazy sequence rather than a list.
Write while loops whose condition is tested before each pass, and reason about what in the body actually drives them to stop.
Control loop flow with break and continue, and use the loop else clause to detect that a loop finished without breaking.
Predict how much work a nested loop really does by multiplying its iteration counts, and cut that work with hoisting or lookup tables.
Use enumerate() to get a running counter alongside each item, choose its start value, and know when the counter is a real index.
Use zip() to walk several sequences in lockstep, unpack the tuples it yields, and control what happens when the inputs have different lengths.
Add or remove dictionary entries around a loop without triggering RuntimeError, by iterating a snapshot or building a new dict.
Build lists with literals or list(), measure them with len(), and read or replace any element using positive and negative indices.
Select any run of items with lst[start:stop:step] and use slice assignment or del to replace, insert, or remove whole regions of a list.
Grow and shrink lists in place with append, extend, insert, pop, remove, del and clear, and predict which call returns a value and which raises.
Find, tally, and order list elements using in, index, count, sort, and sorted, including key functions and reverse.
Tell aliases apart from real copies, copy a list shallowly or deeply on purpose, and trace mutations that change data through a second name.
Build new lists in one expression with map-and-filter comprehensions, and know exactly when a plain loop is the better choice.
Model a table as a list of row lists, index and mutate it with grid[r][c], build grids safely, and gather columns.
Create tuples correctly, predict which operations raise TypeError, and use tuples as dictionary keys and set members.
Unpack any iterable into multiple variables, use a starred target to absorb a variable number of items, and read nested unpacking patterns.
Define namedtuple and typing.NamedTuple record types, read fields by name or index, and build modified copies with _replace.
Build dictionaries with literals, dict(), zip, and fromkeys, and predict exactly which key objects survive.
Read dictionary values safely with [], get, and in, and add or replace keys with assignment, setdefault, update, and |.
Use get, setdefault, pop, popitem and update correctly, and work with keys(), values() and items() as live set-like views.
Iterate a dict over keys, values, or key-value pairs, and build new dicts with comprehensions that filter, transform, or invert data.
Navigate, mutate, and safely probe dictionaries that contain dictionaries and lists, and understand what survives a JSON round trip.
Use defaultdict for grouping and accumulating, Counter for frequencies and multiset math, and OrderedDict for reordering keys.
Build sets in Python, test membership, and combine them with union, intersection, difference, and subset comparisons.
Use frozenset to put set-like values inside sets and dictionary keys, and explain exactly which objects Python considers hashable and why.
Pick between list, tuple, set, and dict by matching the container's shape and lookup cost to the question your code actually asks of the data.
Define functions with def, call them with parentheses, and tell the difference between a function object and the result of running it.
Pass arguments into a function's parameters, return results to the caller, and reason about how names bind to objects across that boundary.
Predict and control how Python evaluates default parameter values, and fix functions that share one mutable default across every call.
Call functions by parameter name, and design signatures with a bare * so some parameters can only be passed by name.
Write functions that accept any number of positional and keyword arguments with *args and **kwargs, and spread sequences and dicts back out at the call site.
Use the / marker to make leading parameters positional-only, so their names stay private and keyword arguments can safely reuse them.
Predict which binding a name resolves to in nested Python functions, and rebind names deliberately with global and nonlocal.
Write docstrings that state a function's contract, and inspect them at runtime through __doc__, inspect.getdoc, and doctest.
Write single-expression anonymous functions with lambda, pass them as sort and filter keys, and know when a def is the better choice.
Pass functions as values to map, filter, sorted, min and max, and control ordering with key functions and tie-breaking tuples.
Write recursive functions with a correct base case, reason about call-stack depth, and know when CPython's 1000-frame limit means you need a loop.
Annotate parameter and return types on your functions, read them back from __annotations__, and know which errors hints catch and which they don't.
Explain what import actually does, treat modules as ordinary objects living in sys.modules, and choose between `import x` and `from x import y` deliberately.
Author a .py file as a real importable module: docstring, public API via __all__, private helpers, and a body that defines rather than does.
Build real multi-file packages, use __init__.py to define a package's public surface, and tell regular packages from PEP 420 namespace packages.
Tell whether a Python file was imported or run directly, and wire a clean main() entry point behind an if __name__ == '__main__' guard.
Navigate the batteries-included standard library: know which module owns a problem, inspect it with dir/help/__file__, and tell stdlib from PyPI code.
Read pip requirement lines as version sets, install into the interpreter you actually run, and pin an environment so installs stay reproducible.
Create, activate, and verify a virtual environment, and explain how sys.prefix and pyvenv.cfg decide where third-party packages land.
Lay out a Python project with a src directory and write a pyproject.toml whose build-system, project, and tool tables actually describe it.
Create classes, build independent instances from them, and track each object's own state using vars(), is, and type().
Write __init__ methods that give every new instance its own attributes with safe defaults, and inspect what actually lands in the instance __dict__.
Write instance methods that read and mutate per-object state through self, and explain why obj.m(x) is exactly Class.m(obj, x).
Predict and control whether an attribute lives on the class or on one instance, and why assignment through an instance never touches the class.
Write @classmethod alternative constructors and @staticmethod helpers, and predict exactly what each one receives when called.
Use @property to make attribute access run code, add validating setters and deleters, and store state in a backing attribute safely.
Create subclasses that reuse a base class's code, override selected methods, and predict which implementation runs when an instance calls them.
Read a class's __mro__, predict which implementation zero-arg super() calls in a diamond, and chain __init__ cooperatively so every class runs.
Combine several base classes deliberately, write reusable mixins, and order bases so their overrides and super() calls actually run.
Write __str__, __repr__, and __eq__ so your objects print readably and compare by value, and know which one Python calls where.
Implement dunder methods like __add__, __eq__, and __lt__ so your own classes work with +, ==, < and sorted(), following Python's operand-fallback rules.
Use @dataclass to generate __init__, __repr__, and __eq__ from annotated fields, and control them with field(), frozen, and order.
Define enforced contracts with abc.ABC and abstractmethod, and structural contracts with typing.Protocol, and pick the right one per situation.
Decide when subclassing is justified and when to hold an object instead, and build wrappers that expose only the API you intend to support.
Tell apart errors that stop a file from running at all and exceptions raised while it runs, and know which ones you can catch.
You can catch runtime failures with try/except, target the exact exception classes you can recover from, and order handlers so specific cases stay reachable.
Use else to run code only when the try block succeeded, and finally to run cleanup no matter how the block exits.
Raise exceptions deliberately with useful messages, and use a bare raise to pass a partly handled exception back to the caller.
Design your own exception types with a shared base class and attached data, so callers can catch failures precisely instead of parsing message strings.
Read a Python traceback from the bottom up and use raise ... from to attach, inspect, or suppress the original cause of an error.
Use assert for internal invariants and real exceptions for input validation, and know why -O makes assert unsafe for checks that must always run.
Record recoverable failures with logging.exception and flag advisory problems with warnings.warn, instead of printing or silently swallowing them.
Choose the right open() mode for a task and predict whether the call creates, truncates, or refuses a file, and whether reads give str or bytes.
Read a text file in one call with read(), or stream it one line at a time by iterating the file object, and know when each is correct.
Create, overwrite, and extend text files with write(), writelines(), and append mode, and control exactly when bytes reach disk.
Use with to guarantee cleanup, read the __enter__/__exit__ protocol, and build your own context managers as classes or generators.
Build, inspect and transform filesystem paths with pathlib.Path: join with /, split names and suffixes, and create parent directories before writing.
Use Python's csv module to write and read delimited files, handle quoted fields and headers, and convert text fields to real types.
Save Python dicts and lists to JSON files with json.dump, read them back with json.load, and predict which types survive the round trip.
Read and write raw bytes with 'rb'/'wb', convert between bytes and str, and inspect or patch individual byte positions.
Create, inspect, copy, move and delete directory trees with os and shutil, and know which module owns which job.
Write files without risking corruption: build the new content in a temp file beside the target, then publish it with one os.replace call.
Use iter() and next() directly to drive any Python iteration by hand, and explain the iterable/iterator split that for loops rely on.
Write your own iterator classes with __iter__ and __next__, and choose between one-shot iterators and re-iterable containers.
Write functions that pause at yield and resume where they left off, and know exactly when the body runs and when iteration stops.
Write generator expressions and predict exactly when each element is computed, including the one part evaluated eagerly and why a second pass yields nothing.
Use yield from to delegate iteration, send/throw/close and a subgenerator's return value instead of hand-writing a forwarding loop.
Compose islice, chain, groupby, accumulate and tee into standard recipes for chunking, sliding windows, dedup and grouping without building lists.
Build set, dict, and nested comprehensions with confidence, and predict exactly when duplicates get collapsed silently.
Measure and explain why a million-item list costs megabytes while an equivalent generator stays a few hundred bytes, and when the list still wins.
Build decorators by hand, knowing that @d above a def is just f = d(f) run once, and write wrappers that stay drop-in replacements.
Build decorators that take their own arguments using a three-layer factory, and preserve the wrapped function's identity with functools.wraps.
Read a closure's cell contents, predict late-binding results in loops, and fix them with default args, factories, or nonlocal.
Build generator-based context managers with @contextmanager, handle exceptions in the with-body correctly, and compose them using ExitStack and suppress.
Use functools.cache/lru_cache to memoize pure functions, partial to pre-bind arguments, and reduce to fold a sequence into one value.
Use re to search, capture, and rewrite text with compiled patterns, named groups, match objects, and non-greedy quantifiers.
Work with aware datetimes in Python using zoneinfo, convert between zones, and reason correctly about DST gaps, folds, and durations.
Write generic classes and functions with TypeVar, model absent values with Optional, and type by structure using Protocol.
Choose threads or processes for a given workload by reasoning about when CPython's GIL is held and when it is released.
Run I/O-bound and CPU-bound Python work in parallel with concurrent.futures, and collect results or exceptions safely from Future objects.
Run async code with asyncio.run, understand exactly what await suspends, and overlap waiting work with gather instead of sequential awaits.
Run coroutines concurrently with gather, bound them with wait_for or asyncio.timeout, and handle CancelledError so cleanup still runs.
Decide which code deserves tests first and turn risky branches, boundaries, and bug reports into concrete input/expected cases.
Write and run stdlib unittest tests: TestCase subclasses, test_ methods, assertEqual/assertRaises, setUp isolation, subTest, and reading the report.
Write pytest tests that share setup through fixtures and cover many inputs with @pytest.mark.parametrize, then read the run report.
Isolate code from networks, clocks and filesystems by injecting fakes or patching the name where it is looked up, using autospec to keep doubles honest.
Read a coverage report critically and write assertions that pin exact behaviour, so a passing suite is real evidence the code works.
Configure ruff and black in pyproject.toml, and know which problems a formatter can fix, which ones only the linter reports, and in what order to run them.
Stop a running Python program at a chosen line with breakpoint(), inspect frames and locals in pdb, and control or disable it via PYTHONBREAKPOINT.
Explain and demonstrate why a NumPy array uses less memory and computes faster than an equivalent Python list, and when a list is still the right choice.
Create NumPy arrays with array, zeros, arange and linspace, and choose a dtype deliberately so range, precision and memory fit your data.
Index and slice NumPy arrays along multiple axes, and predict when the result shares memory with the original versus copies it.
Select and modify array elements with boolean masks and integer index arrays, and know when NumPy hands back a copy instead of a view.
Use NumPy ufuncs to do element-wise math on whole arrays, control result dtype and memory with out= and where=, and predict inf/nan and overflow.
Predict the result shape of any element-wise NumPy operation, and use newaxis or keepdims to control how shapes line up.
Reshape arrays without copying data, join them with concatenate or stack along the right axis, and split them back into views.
Predict and control the shape of any NumPy reduction by treating axis as the dimension that gets collapsed, and use keepdims to broadcast results back.
Use numpy.linalg to solve linear systems, compute determinants, norms, eigenvalues and least-squares fits, and pick between solve, lstsq and pinv.
Create seeded NumPy Generators with default_rng, reason about how seed and draw order fix results, and spawn independent streams for parallel work.
Build Series and DataFrames from scratch, read their index and dtype metadata, and predict how label alignment fills or drops values.
Load CSV, Excel, and JSON into DataFrames and steer the parser with dtype, parse_dates, na_values, sep, sheet_name, and json_normalize.
Select exact rows and columns by label with .loc and by integer position with .iloc, and know which one a given task needs.
Build boolean masks from column comparisons and use them to select DataFrame rows, combining conditions with &, |, ~ and isin.
Add derived columns, rename them safely, and drop the ones you no longer need, while tracking what pandas mutates and what it copies.
Detect, count, drop, and fill missing values in pandas, and predict how NaN changes aggregations and column dtypes.
Convert pandas columns between string, numeric, nullable-integer, and category dtypes, and control how categorical order drives sorting and comparison.
Reorder rows by column values or index labels, control tie-breaking and NaN placement, and attach 1-based rank columns with the rank rule you need.
Split a DataFrame by column values, aggregate or transform each group, and control whether keys become the index or stay as columns.
Summarise data with .agg using strings, lists, and dicts, and use named aggregation to get flat, predictably ordered output columns.
Combine pandas objects with concat, merge, and join, pick the right join type, and diagnose rows that vanish or multiply.
Turn a date column into a DatetimeIndex, slice it with partial date strings, and aggregate to any frequency with resample.
Create Matplotlib figures and axes explicitly, know which object owns which method, and stop relying on pyplot's hidden current-axes state.
Draw line plots from lists and records with ax.plot, control ordering and sampling, and understand why a line looks the way it does.
Draw, order, group, stack, and label bar charts of categorical data in Matplotlib, and reason about the numeric slots behind category names.
Draw histograms with ax.hist, choose bin edges deliberately, and switch between counts and density so the bars describe the distribution honestly.
Plot paired measurements as a cloud of markers, encode extra variables with size and colour, and read a Pearson r off the shape.
Label axes, control what a legend shows, set tick positions and their text format, and attach arrow annotations to specific data points.
Build multi-panel Matplotlib figures with plt.subplots, link panels using sharex/sharey, and remove the redundant inner tick labels.
Control Matplotlib's look through rcParams and styles, pick colours that match the data's meaning, and save figures at exact pixel sizes.
Explain how SciPy layers domain algorithms on NumPy's ndarray, import its subpackages correctly, and choose between scipy.linalg and numpy.linalg.
Fit parameterised models to data with scipy.optimize.curve_fit and minimise your own objective functions, then judge whether the result is trustworthy.
Build interpolants through sampled data with np.interp, CubicSpline and RegularGridInterpolator, and judge when each one is trustworthy.
Use quad and the sampled-data rules for definite integrals, and solve_ivp for ODE initial-value problems, including tolerances, events and stiff methods.
Query SciPy's distribution objects (pdf, cdf, ppf, fit) and run t-, chi-square and rank tests, reading a p-value as a tail area under the null distribution.
Build and multiply SciPy sparse matrices in the right format, and see how an FIR filter is the same thing as a banded matrix.
Model a problem as related tables with keys and constraints, then let SQL do the joining and aggregating instead of Python loops.
Use sqlite3 as a concrete DB-API 2.0 driver: connections, cursors, description, rowcount, the fetch methods, and the standard exception tree.
Open, configure and close a MySQL connection from Python with a DB-API driver, and tell apart the errors a failed connect raises.
Design a SQLite table whose constraints reject bad data, then insert rows using executemany, lastrowid, ON CONFLICT, and IntegrityError handling.
Run SELECT queries from Python with qmark and named placeholders, and see exactly why bound parameters make SQL injection impossible.
Group related SQL writes into all-or-nothing transactions in Python, committing on success, rolling back on failure, and undoing parts with savepoints.
Model data as BSON documents, navigate PyMongo's client/database/collection handles, and predict which Python types survive a round trip.
Insert, read, update, and delete MongoDB documents from Python with PyMongo, and read the result objects each write returns.
Build MongoDB aggregation pipelines from PyMongo and create indexes that the early stages of those pipelines can actually use.
Decide between normalized tables and embedded documents by mapping your read and write units, then model the same data both ways in Python.
Trace how Django boots a project: one settings dotted path, django.setup(), the app registry, and the lazily resolved strings that wire it together.
Install Django into a virtual environment, generate a project skeleton with startproject, and run the development server with confidence about what each command does.
Organize a Django codebase: register apps through AppConfig and INSTALLED_APPS, control app labels, and lay out settings so each environment loads one module.
Trace a Django request from URLconf to view to HttpResponse, and write path() routes with converters, named URLs, and correct status codes.
Build a base.html skeleton with {% block %} holes and write child templates that extend it, overriding or appending to blocks with {{ block.super }}.
Declare Django models with the right field types and options, and know where each option acts: the database column, Python conversion, or validation.
Understand how Django builds migrations by diffing rebuilt model state against models.py, and apply, inspect, and reverse them safely.
Build, chain, and evaluate Django QuerySets with filter, exclude, and field lookups, and know exactly when a query hits the database.
Model one-to-many and many-to-many links in Django with ForeignKey and ManyToManyField, then traverse them in both directions with the ORM.
Register models with ModelAdmin classes to get a staff CRUD interface, and tune the changelist and change form with list_display, fieldsets and inlines.
Define Django Form classes, validate untrusted request data through the field/clean_<field>/clean pipeline, and read typed values from cleaned_data.
Create users with correctly hashed passwords, log them in with authenticate and login, and gate access using groups, permissions, and has_perm.
Configure STATIC_URL, STATIC_ROOT, STATICFILES_DIRS, MEDIA_ROOT and DEBUG so assets and uploads work both under runserver and in production.
Write and run Django tests that use a throwaway test database, the test Client, and reverse() to check views, models, and data isolation.
Turn a Python function into a step count, name its Big-O class, and confirm the class with doubling tests and timeit measurements.
Explain how a Python list works as a dynamic array of object references, and predict which list operations are cheap and which are linear.
Build singly linked lists from node objects, splice and unlink nodes correctly, and explain why traversal is O(n) while insertion at a held node is O(1).
Use a Python list as a LIFO stack to match brackets, track pending work, and replace recursion with an explicit loop.
Build correct FIFO queues in Python with collections.deque, know why list.pop(0) is the wrong tool, and when queue.Queue is needed instead.
Explain how CPython turns a key into a table slot, why lookups still compare keys, and write classes whose __hash__ and __eq__ agree.
Model trees with a node class, traverse a binary tree four ways, compute its height, and explain why shape rather than node count decides cost.
Build, search, and delete keys in a binary search tree in Python, and explain why insertion order decides whether lookups cost log n or n.
Use heapq to keep the smallest item of a growing collection reachable in O(1) and push/pop in O(log n), including max-heaps and top-k patterns.
Build a graph as an adjacency dict, adjacency matrix, or edge list, and pick the representation from the graph's density and query pattern.
Implement iterative BFS with a deque and DFS with a stack or recursion, and use BFS parent pointers to recover shortest paths in unweighted graphs.
Implement insertion sort and merge sort by hand, and use key= plus stability rules to control how Python's built-in sort orders records.
Implement binary search with a correct loop invariant, use bisect for insertion points, and solve sorted-array problems with converging or same-direction pointers.
Recognise overlapping subproblems in a recursion, cache them with a memo, and convert the result into a bottom-up dynamic programming loop.
Decide whether a problem is learnable by testing three hard limits: extrapolation, association vs cause, and the error floor your labels impose.
Use scikit-learn's estimator contract: construct with hyperparameters, fit on training data, read underscore attributes, predict, and wrap it in a Pipeline.
Load a dataset into an X/y pair and carve out an honest hold-out test set with train_test_split, using random_state, stratify and shuffle correctly.
Fit scalers on the training split only, transform everything else with them, and pick StandardScaler, MinMaxScaler or RobustScaler on purpose.
Fit, read and sanity-check a least squares linear regression in scikit-learn, and say what each coefficient and the R^2 score actually mean.
Fit a logistic regression in scikit-learn, read its probabilities and log-odds, and choose a decision threshold on purpose.
Train and tune decision trees and random forests in scikit-learn, compare train and test accuracy, and read feature importances.
Predict labels with KNeighborsClassifier from nearby examples, group unlabelled points with KMeans, and keep the two meanings of k apart.
Read a confusion matrix and choose between accuracy, precision, recall, F1, MAE, RMSE and R2 based on which errors actually cost you something.
Score models with k-fold cross-validation and pick hyperparameters with GridSearchCV or RandomizedSearchCV without leaking your test set.
Tell underfitting from overfitting by comparing training and held-out error, and use Ridge/Lasso alpha to trade training fit for generalisation.
Persist a fitted scikit-learn pipeline with joblib, reload it safely, and report a test score that is not inflated by tuning.
Build a CLI data cleaner in Python where pure per-field cleaning functions return values plus problem reports, wired up with argparse at the edges.
Build a Python file organiser that plans every move first, then applies them, sorting by extension or date without ever overwriting a file.
Turn a messy CSV into a saved PNG chart: coerce types at the read boundary, aggregate into x-axis keys, then draw.
Build a Django app whose models define the schema: create the tables, query with the ORM, and keep data integrity in the database.
Evaluate a classifier honestly: hold rows out, compare against a majority-class baseline, and report confusion-matrix metrics instead of bare accuracy.
Audit which parts of Python you actually use, choose your next area from evidence rather than hype, and turn working scripts into maintained code.