Course background and goals
If you've completed the prerequisite first-year courses in ICS, but otherwise have never studied programming before, then Python is likely to be the only programming language you've ever experienced. If so, that's not a bad thing; it's probably better to thoroughly study one programming language for a while — especially one that offers as much variety and customizability as Python does — so that you can plumb its depths before trying to wrap your head around conflicting concepts and alternative ideas about what it means to write quality programs. Arguments about programming languages and their features are commonplace and can be both fascinating and enlightening, but they can also be circular — with two groups of people that don't see eye-to-eye and probably never will — and can distract from the core goal of learning and applying those techniques to solve problems. So, when you're first starting out, learning one coherent set of techniques, regardless of whether it ends up being your favorite choice in the decades to come, is paramount. And, truth be told, most of what you're learning in these first-year courses is more universal than you might realize, even if it's expressed in terms of the design of Python and its standard library. (I haven't written a Pascal program in nearly thirty years, as of this writing, but many of the important lessons I learned when I plumbed Pascal's somewhat less impressive depths as a first-year undergraduate remain embedded in my thinking to this day.)
Going forward, once you have a firm understanding of a Pythonic way of writing programs, you'll want to seek out new ideas and fresh techniques, contrasting them against the ones you learned initially, while remaining as open-minded as you can; just because something is different from what you first learned won't necessarily make it wrong, even if its unfamiliarity will make it a little uncomfortable at first. Exposure to a broader set of approaches will make it easier to learn new ones as the years pass; the more you know, the more often a new idea connects to an idea you already understand. In our mainstream computing curricula in ICS, we ask students to do that in their second year, by offering courses that focus on programming languages other than Python. Python is far from the final word on programming language design, and the final word almost certainly hasn't been written yet. Since the days when I was first learning to program, many ideas have gained and lost favor, and a few "silver bullets" have flown by without hitting their targets. Popular programming techniques from the late 20th century have become passé, while techniques that were considered niche or impractical in those days are now fashionable. Times change, same as it ever was.
The most basic goals of programming haven't changed over the years, though. We still want the same things we did in decades past.
- We want our programs to meet their functional requirements, which is to say that they do what we need them to do.
- We also want our programs to meet their non-functional requirements, such as those around performance, robust handling of erroneous input, security, and compliance with legal and regulatory requirements.
- We want it to be possible to evolve our programs inexpensively as these requirements change, while preserving as-is the features that weren't meant to change. (University assignments are usually submitted, graded, and forgotten about; real-world programs more often evolve over years and sometimes decades.)
While there are numerous techniques that we can use to achieve these goals — this conversation ultimately leads to multiple courses' worth of material on its own — there are a couple of big-picture ideas that we can keep in mind now.
- One of the simplest and most powerful ideas in software design is "keep separate things separate," so that a large and complex program is cleanly divided into interacting parts that are as independent as possible from each other. The more we know about a programming language's features, the more ways we have to isolate the components that make up our programs, while allowing exactly and only the interactions that are necessary between them.
- All things being equal, not writing code is better than writing it, but all things are not always equal. This is a glib way of saying that even though we'd rather not build things from scratch, sometimes we have to. But the more we know about a programming language and its library, the fewer things we'll need to build from scratch.
- The tools we need might already be built, in which case we just need to use them.
- The tools we need might be so similar to each other that we can build one higher-level tool instead of many lower-level ones.
- The tools we need might be so formulaic that their code can be generated automatically instead of written by hand.
- Knowing whether a program works revolves around testing it. Testing is often boring and repetitive work, which is exactly the kind of work that computers excel at most, so we should automate it when we can. That way, we can test it quickly and repeatedly whenever it changes, rather than testing it haphazardly and manually when the mood strikes us.
So, you'll eventually want to branch out and broaden your horizons, and future courses will help you to do that. But, before you go, Python still has many important lessons to offer, especially now that you've achieved a level of familiarity with it. Being able to write a Python program isn't the same as being able to write a Pythonic one. Being able to write a program that barely works isn't the same as being able to write one that meets its requirements and whose design will stand the test of time. To enable you to do those things, we'll need to plumb Python's depths, embracing its complexities where we need them, understanding some of the details of how Python programs are executed behind the scenes, as well as how we can influence those details to write better code (and, oftentimes, avoid writing code at all). The way you express a program doesn't have to be driven by the most basic constructs a language offers or solely by what's available in its standard library. As a programmer, if the tools you have available don't solve your problem, it's better to build your own tools that do, rather than struggle with the tools provided to you. But that tool-building requires knowledge.
- You'll need to be able to use a wider variety of a language's features.
- You'll need a more thorough understanding of what happens when you use those features.
- You'll need to know how to customize their behavior, so that you can build exactly the tool you need.
- You'll need to be familiar with what's built into Python and its standard library, so you can avoid reinventing things that already exist, and so your tools will combine naturally with those that are built in.
- You'll need to know how to quantify the costs of running a program, so you can formulate an understanding of how you might minimize them.
So, that's where our focus will be in this course. Before moving on to new places, let's be sure you've learned enough about Python to use it effectively. You'll be surprised later how many of the same ideas will appear in other programming languages you learn as the years unfold.
My hope is that it'll be a rewarding trip, but it won't be an easy one. Most rewarding trips aren't; that's partly what makes them rewarding.
A brief tour of where we're going
So, given the goals we have in mind, what's on the menu for this course? What will we learn that will enable us to meet those goals?
- We'll more thoroughly explore the collection of namespaces that Python uses when it decides what we mean when we use a name like
boo
in a program.
- We'll dig into the details of how classes and their objects are different from each other, as well as the ways that classes influence the behavior of their objects.
- We'll take a look at the various ways that Python allows us to customize the mechanics of how functions are called, by more flexibly specifying both their arguments and their parameters.
- We'll consider how we might automate wrap-up tasks such as closing files, how many ways they can be used besides just closing files, then examine the tools Python provides to make that automation less error-prone.
- We'll learn how to analyze the algorithms we're planning to write using asymptotic analysis — in many cases before we write them — so we can quantify how long they might take to run, or how much available memory will be necessary to run them, then use that understanding to find ways to dramatically reduce those requirements.
- We'll accept that not all problems are best solved in Python, by building an initial understanding of relational databases and how their use is different from what you've seen in Python previously.
- We'll investigate how additional Python language features can help us to succinctly and performantly build and iterate data structures.
- We'll take a deep dive into the Python data model, which describes in detail the means by which we can interact with — and customize how we interact with — objects in a Python program.
- We'll revisit the concept of recursion in more depth, so we can better understand when it might be a good design choice and when it might best be avoided.
- We'll embrace a set of techniques known as functional programming and see how it might be able to improve our ability to write programs in Python.
- We'll improve our ability to write separate, reusable pieces of programs by writing functions whose job is to transform other functions for us, automating code changes we might otherwise have to make by hand repeatedly.
- We'll focus our attention on how to build tools that resist accidental misuse, making it as easy as possible to use them correctly and as difficult as possible to make mistakes with them.
Along the way, we'll ask you to solve problems both large and small, with weekly exercises that will gauge your understanding of the course material as we move forward, along with larger-scale projects that will let you put new techniques into realistic practice. There will be a lot of code to write, and we expect that not everyone will complete every task successfully, but we hope by the end of the course that you'll have built a wide variety of new skills, that we've been able to spark some new excitement about computing, and that you'll be looking forward to future courses that build on this one.
What we expect of you
There are three things we expect of you as we set off on our journey.
- You'll need a Python background equivalent to what a student will have gained by successfully completing either our ICS 31 and ICS 32 courses, or, alternatively, our ICS H32 (formerly 32A) course. A complete overview of what that means can be found by looking through the Notes and Examples from my most recent ICS H32 offering, which will include all of the Python knowledge you'll be expected to have at the outset of this course. It's natural that there will be some areas where your knowledge is stronger than others, but if the majority of the listed topics appears to be new for you, you may find this course to be a struggle, because we'll be assuming facility with those topics.
- You'll need to supply the natural curiosity to want to learn what we're teaching, which is not simply a set of facts to be memorized or rote procedures to follow, but instead is a collection of tools and an understanding of how to apply those tools to solve problems you haven't seen before. We'll light the way with as much enthusiasm as we have to offer, but you'll still have to hold up your end of the bargain, putting in the time to read and experiment, asking questions when you have them (but learning how to answer your own questions in the Python shell when you can), and focusing on obtaining long-term knowledge and skills instead of worrying solely about points in a grading spreadsheet.
- You'll need to take care of yourself this quarter, which means managing your time efficiently, so you can make sure that you're able to get the rest you need along the way, and so that topics that simply require calendar time to understand will be accessible. Starting assignments early, reading notes ahead of lectures, and otherwise being forward-looking is something that really pays off, while constant last-minute work usually leads to multiple kinds of trouble — not being able to finish one's work on time, worrying more and learning less, not sleeping enough to be able to function effectively, and the mental and physical health consequences that come along with those things. Succeeding in a course like this one is not just a matter of being smart, or having a strong academic track record; plenty of smart and talented students fail my courses every quarter by mismanaging their time early, falling behind in their understanding, and finding it impossible to catch back up.
Course organization and logistics
This course web site describes the logistical details of how this course is going to be run. Particularly, be sure that you read through the Course Reference, the front page of the Project Guide, and the front page of the Reinforcement Exercises section, so you'll know how this course operates, what's expected of you each week, and how you'll be doing and submitting your work along the way. We want you to do as well in the course as you can, but we won't be watching over your shoulder, and we won't be able to offer a bespoke course for each of you; it'll be up to you to attend class when it's scheduled, complete and submit the assigned work before due dates, read and understand assignment requirements, and so on.