Monday, June 23, 2025

Proficient Python: A free interactive online course

There are many ways to learn Python online, but there are also many people out there that want to learn Python for multiple reasons - so hey, why not add one more free Python course into the mix? I'm happy to finally release ProficientPython.com, my own approach to teaching introductory Python.

The course covers standard intro topics - variables, functions, logic, loops, lists, strings, dictionaries, files, OOP. However, the course differs in two key ways from most others:

  • It is based on functions from the very beginning (instead of being based on side effects).
  • The coding exercises can be completed entirely in the browser (no Python setup needed).

Let's explore those points in more detail.

A functions-based approach

Many introductory programming courses teach first via "side effects", asking students to either print out values to a console, draw some graphics, manipulate a webpage, that sort of thing. In fact, many of my courses have been side-effects-first, like my Intro to JS on Khan Academy that uses ProcessingJS to draw pictures, and all of our web development workshops for GirlDevelopIt. There's a reason that it's a popular approach: it's fun to watch things happen! But there's also a drawback to that approach: students struggle when it's finally time to abstract their code and refactor it into functions, and tend not to use custom functions even when their code would benefit from them.

When I spent a few years teaching Python at UC Berkeley for CS61A, the first course in the CS sequence, I was thrown heads-first into the pre-existing curriculum. That course had originally been taught 100% in Scheme, and it stayed very functions-first when they converted it to Python in the 2000s. (I am explicitly avoiding calling it "functional programming" as functional Python is a bit more extreme than functions-first Python.) Also, CS61A had thousands of students, and functions-based exercises were easier to grade at scale - just add in some doctests! It was my first time teaching an intro course with a functions-first approach, and I grew to really appreciate the benefits for both student learning and classroom scaling.

That's why I chose to use the same approach for ProficientPython.com. The articles are interweaved with coding exercises, and each exercise is a mostly empty function definition with doctests. For example:

Screenshot of coding exercise for function called lesser_num, with doctests and no body

When a learner wants to check their work, they run the tests, and it will let them know if any tests have failed:

Screenshot of coding exercise for function called lesser_num, with doctests and an incorrect function body, plus test results that say 2 out of 3 tests passed

Each unit also includes a project, which is a Jupyter notebook with multiple function/class definitions. Some of the definitions already have doctests, like this project 2 function:

Screenshot of Jupyter notebook with a function definition with multiple tests

Sometimes, the learners must write their own doctests, like for this project 1 function:

Screenshot of Jupyter notebook cell with a function definition with a single test

When I'm giving those projects to a cohort of students, I will also look at their code and give them feedback, as the projects are the most likely place to spot bad practices. Even if a function passes all of its tests, that doesn't mean it's perfect: it may have performance inefficiencies, it may not cover all edge cases, or it just may not be fully "Pythonic".

There's a risk to this functions-based approach: learners have to wrap their minds around functional abstraction very early on, and that can be really tricky for people who are brand new to programming. I provide additional resources in the first unit, like videos of me working through similar exercises, to help those learners get over that hump.

Another drawback is that the functions-based approach doesn't feel quite as "fun" at first glance, especially for those of us who love drawing shapes on the screen and are used to creating K-12 coding courses. I tried to make the exercises interesting in the topics that they tackle, like calculating dog ages or telling fortunes. For the projects, many of them combine function definitions with side effects, such as displaying images, getting inputs from the user, and printing out messages.

Browser-based Python exercises

As programming teachers know, one of the hardest parts of teaching programming is the environment setup: getting every student machine configured with the right Python version, ensuring the right packages are installed, configuring the IDE with the correct extensions, etc. I think that it's both important for students to learn how to set up their personal programming environment, but also that it doesn't need to be a barrier when initially learning to program. Students can tackle that when they're already excited about programming and what it can do for them, not when they're dabbling and wondering if programming is the right path for them.

For ProficientPython.com, all of the coding can be completed in the browser, via either inline Pyodide-powered widgets for the exercises or Google CoLab notebooks for the projects.

Pyodide-powered coding widgets

Pyodide is a WASM port of Python that can run entirely in the browser, and it has enabled me to develop multiple free browser-based Python learning tools, like Recursion Visualizer and Faded Parsons Puzzles.

For this course, I developed a custom web element that anyone can install from npm: python-code-exercise-element. The element uses Lit, a lightweight framework that wraps the Web Components standards. Then it brings in CodeMirror, the best in-browser code editor, and configures it for Python use.

When the learner selects the "Run Code" or "Run Tests" button, the element spins up a web worker that brings in the Pyodide JS and runs the Python code in the worker. If the code takes too long (> 60 seconds), it assumes there's an infinite loop and gives up.

Screenshot of function definition with a while True loop and output that says the program took too long

If the code successfully finishes executing, the element shows the value of the final expression and any standard output that happened along the way:

For a test run, the element parses out the test results and makes them slightly prettier.

The element uses localStorage in the browser to store the user's latest code, and restores code from localStorage upon page load. That way, learners can remember their course progress without needing the overhead of user login and a backend database. I would be happy to add server-side persistence if there's demand, but I love that the course in its current form can be hosted entirely on GitHub Pages for free.

Online Jupyter notebooks

The projects are Jupyter notebooks. Learners can download and complete them in an IDE if they want, but they can also simply save a copy of my hosted Google CoLab notebook and complete them using the free CoLab quota. I recommend the CoLab option, since then it's easy for people to share their projects (via a publicly viewable link), and it's fun to see the unique approaches that people use in the projects.

I have also looked into the possibility of Pyodide-powered Jupyter notebooks. There are several options, like JupyterLite and Marino, but I haven't tried them out yet, since Google CoLab works so well. I'd be happy to offer that as an option if folks want it, however. Let me know in the issue tracker.

Why I made the course

I created the course content originally for Uplimit.com, a startup that initially specialized in public programming courses, and hosted the content in their fantastic interactive learning platform. I delivered that course multiple times to cohorts of learners (typically professionals who were upskilling or switching roles), along with my co-teacher Murtaza Ali who I first met in UC Berkeley CS61A.

We would give the course over a 4-week period, 1 week for each unit, starting off each week with a lecture to introduce the unit topics, offering a special topic lecture halfway through the week, and then ending the week with the project. We got great questions and feedback from the students, and I loved seeing their projects.

Once Uplimit pivoted to be an internal training platform, I decided it was time to share the content with the world, and make it as interactive as possible.

If you try out the course and have any feedback, please post in the discussion forum or issue tracker. Thank you! 🙏🏼

No comments: