Python Crash Course, 2nd Edition
This book review was quoted by the publisher.
What is the best way to determine the popularity of any particular computer programming language? Technical question-and-answer forums can report statistics on the relative number of posts connected to that language versus the alternatives, as well as changes in that ranking over time. The same can be gleaned from Internet search engines as to the total number of web pages or searches for information about the given language. Yet perhaps a more telling statistic would be the number of instructional books sold, because only that one reflects an accumulation of average students of the language exchanging money in the hopes of learning.
A fine example of this is the sales record of the first edition of Python Crash Course, authored by Eric Matthes and published by No Starch Press at the beginning of November 2015. In the three and a half years that followed its publication, it sold more than half a million copies, with translations in eight languages — an achievement rarely seen in the realm of computer programming books. Python may have stumbled with the difficulties resulting from version 3 not being backward compatible with version 2, but aside from that, the language and its community appear to be strengthening and spreading worldwide. Numerous universities choose Python as a learning language for their students, and countless corporations and other organizations choose Python for a wide variety of software projects.
Experienced and neophyte programmers alike who wish to get up to speed on this popular language, may turn to the second edition of the book, published on May 21, 2019, under the ISBN 978-1593279288. It is available in both print and electronic form. (This review is based on the ebook version generously provided by the publisher.) Eric Matthes has apparently been writing computer programs since he was a child, and as an adult now teaches children how to do the same, using Python. Thus it would seem he is well-qualified to teach the language to beginners, as this book aims to do. In his preface to this newest incarnation, he notes that "There is no longer any reason to learn Python 2, so this edition focuses on Python 3 only."
Python Crash Course immediately appears substantial, as it comprises 544 pages. It is organized into two major parts — Basics and Projects — each of which is divided into eleven and nine chapters, respectively. In addition, the book has four appendices. On the publisher's web page, visitors can learn more about the book, read a few sample pages, download the second chapter, and read the table of contents as well as some comments by other reviewers.
In the book's introduction, the author briefly describes how he first became interested in computer programming, and then explains who the book is intended for and what the reader can expect to learn from it. He mentions additional resources, beyond the book itself, including the example source code files, setup instructions, updates to the same, exercise solutions, and cheat sheets for quick reference. These are available online, at two different web pages: the first one is the aforementioned publisher's page and the second is part of the author's GitHub account. Readers should download at least the example source code, to facilitate trying that code on their own machines; specifically, they can avoid having to copy and paste the code from the e-book or, even worse, typing it from the printed page, which would slow down the process and increase the odds of unintentionally ending up with missing or mis-typed source code. He finishes the introduction by presenting reasons why the reader should choose Python and continue using it despite the emergence of newer languages.
As noted earlier, the first part of the book lays out the syntax and other basic concepts of Python. The first chapter begins with a brief discussion of Python versions, code snippets, and the Sublime Text editor. A questionable part of the narrative is the claim that "If it [a "Hello world" program] runs correctly on your system, any Python program you write should work as well" (page 4). This is only true if any packages required by that program have also been installed, and they will likely be absent unless explicitly added. Another misleading statement, on the next page, in the section about Windows, is that "If you see an error message telling you that python is not a recognized command, Python isn't installed." More correctly, it could be installed but the directory containing the Python executable might simply not be a part of one's Windows PATH environment variable. The reader is encouraged to install the latest version of Python (3.7.2 in the book) and Sublime Text, following the appropriate steps corresponding to the reader's operating system — Windows, macOS, or Linux. At the end of this section for Linux, it appears that the author is assuming that every Linux installation has an Ubuntu Software Center, which may be true but seems unlikely to me. After readers have run a "Hello world" program on the command line using the Python interpreter, they may be puzzled by the subsequent section heading, "Running a Hello World Program". But this is not redundant material, because readers are at that point encouraged to run a simple program within their chosen text editor (Sublime Text is used as a proxy). For anyone having trouble getting their program running, the troubleshooting suggestions provided could be helpful. Returning to the command line, the chapter concludes with information on how to run a Python program that is not typed in at the moment but instead already present in a file. Actually, the chapter concludes with a brief and completely useless summary. It and all subsequent summaries should be excised from future editions of the book. Before leaving this chapter, it should be noted that on page 12, the given Windows command cd Desktop\python_work is invalid and should instead be something like cd Users\[username]\Desktop\python_work.
Because the second chapter focuses on variables and simple datatypes, programmers experienced with other languages will likely be able to skim rapidly through this material. PHP and Perl programmers will primarily need to overcome the habit of prefixing variable names with dollar signs and terminating lines with semicolons. Incidentally, the author may have unintentionally invented a wonderful new oxymoron, "variable rules" (page 17). At a pace appropriate for readers new to programming, the author explains the essentials of: naming variables; printing and applying methods to strings; using variables and white space characters in strings; setting integers, floats, and constants; and adding comments. The author wisely supplements the earlier technical exposition with his thoughts on "The Zen of Python" – principles of quality coding by Tim Peters.
Lists (Python's answer to the indexed arrays of other programming languages), tuples, and techniques for looping through them, are fully explicated in the third and fourth chapters. All of the narrative is clear enough, with the exception of the statement "if you want to use an item as you remove it, use the
pop() method" (page 41), which would be better stated as "if you want to use an item after you have removed it, use the
pop() method". More importantly, when discussing the use of the
sort() method and the
sorted() function, it would be best to briefly explain the differences between methods and functions, or at least point the newbie reader to upcoming material that could resolve any possible confusion or questions. The discussion of indentation, as well as examples of common indentation errors, nicely exemplify the author's background in teaching — in this case, forewarning his readers and thus reducing the chances of their making simple formatting errors and in turn experiencing frustration. The chapter concludes with good tips on quality styling of Python code, based upon Python Enhancement Proposal (PEP) 8, to maximize the readability of one's code. The only puzzling rule is the admonition to limit lines to 79 characters, which ignores the capability of any decent programmer's editor to automatically wrap text at any prescribed line length.
Conditional statements are an essential part of any programming language, and the most commonly used type of them are
if statements, which are covered in the fifth chapter. Such a narrow topic may seem at first glance to be insufficient to fill an entire chapter, but readers also learn about equality and inequality tests, numerical comparisons, list inclusion, Boolean values and expressions,
elif statements, empty lists, and a bit more about recommended code styling.
Dictionaries (Python's answer to the associative arrays of other programming languages) are explored in Chapter 6. The author notes that a dictionary is a collection of key-value pairs, and each value can be a number, string, list, or dictionary; but he neglects to explain exactly what a key can be — namely, a number or string, but not a list or dictionary because those two latter types are "unhashable". On page 103, the author states that the
keys() method returns a list of all the keys (of any dictionary). While it is understandable that one would want to minimize the complexity encountered by any fledgling programmer, the author's statement is not strictly true, because the
keys() method actually returns a view object of the dictionary's keys, which is different (although it behaves like a list in this context). This can be confirmed using the text's definition of the
favorite_languages dictionary followed by the line of code
print( str( type( favorite_languages.keys() ) ) ), which outputs
<class 'dict_keys'> and not
<class 'list'>. The same is true on the next page regarding the
values() method. Readers then learn how to loop through dictionaries' keys, values, and key-value pairs — and lastly how to nest dictionaries within lists, lists within dictionaries, and dictionaries within dictionaries — making possible a wide variety of custom data structures.
In many computer language books,
while loops are often introduced at the same time as
if statements, since both are frequently needed and used for controlling the flow within a program. But in this book,
while loops are presented in conjunction with user input, which at first glance may seem odd, but actually makes sense given the coding examples as well as this book's overall tutorial approach. For any reader who is trying the book's example code in the Sublime Text editor, the author warns that it and many other editors don't run programs that prompt the user for input (page 114). It would be more correct to state that basic, "out-of-the-box" installations of those editors will run Python programs containing
input() calls but do not properly allow user input when the editor's debugger reaches those statements. In the case of Sublime Text, there are several techniques for modifying one's installation to add such a capability, e.g., using SublimeREPL Interested readers can search online for that and other solutions. An excellent alternative to Sublime Text is Komodo IDE, which supports interactive debugging of code written in Python and all other major languages, and even supports user input for any
input() calls without the need to modify the program installation. Continuing with the text, it can be argued that the example code to illustrate a
while loop (page 118) is a poor choice because the code simply counts from 1 to 5 and thus any alert reader may immediately wonder why a
for loop wasn't used instead. A better example scenario would be a Boolean condition that controls the looping until its value is changed within the loop as a result of some other factor. The author writes that "you shouldn't modify a list inside a
for loop because Python will have trouble keeping track of the items in the list" (page 124), but no explanation is given as to why this would be true.
Functions (or methods, in object-oriented code) are a critical part of any substantial program, and they are the subject of Chapter 8. Readers learn how to define functions and how to pass various types of values into those functions as positional and keyword arguments. All of the discussion is clear, except the statement "you enter
username in the parentheses of the function's definition at
def greet_user()" (page 130), which makes it sound as though that is the variable name one must utilize when passing a user's name to a function. More clear would be something like "you create and set the value of a variable to represent a user's name — in this case
username — and then put that variable within the parentheses of the function's definition at
def greet_user()". Effective technical writing anticipates how readers may misinterpret what has been written. Two other such examples are the references on page 147 to a "list of toppings", which could be a misleading choice of terms since the functions return tuples and not lists. Readers may get a chuckle out of the file name
pizza.py. The chapter is wrapped up with explanations as to how to import all of the functions from a module file or just some of them, and how to provide aliases for function names and module names. Most of the book's chapter summaries are brief and, as noted earlier, serve no purpose, and thus should be dropped from the book. But in the case of this chapter, its summary contains good advice on the use and value of functions, and that counsel should be moved into the chapter proper.
One of Python's many strengths is its outstanding support for object-oriented programming, which is introduced in Chapter 9. For those new to programming, it can prove to be one of the most difficult topics, so it is fortunate that the author patiently explains and illustrates how to model a real-world object using a class, the purpose and structure of the constructor method (although inexplicably the author does not use that term), how to create a single object or multiple instances of the same class, as well as how to access attribute values and call methods. The only substandard part of the narrative is the misleading statement "When an instance is created, attributes can be defined without being passed in as parameters. These attributes can be defined in the
__init__() method, where they are assigned a default value" (page 163), which makes it sound as though the other attributes were defined by their presence as parameters, which is false. For instance, the Car's make attribute is defined by the statement
self.make = make and not the parameter in the constructor's calling signature, which actually could have been given a different name, e.g.,
self.make = car_make. That paragraph would be better written as "When an instance is created, attributes defined in the constructor
__init()__ can be assigned default values instead of assigned values passed in as parameters." Readers then learn how to store individual and related classes in separate module files and later import them when needed, how to import classes from one module into another, and how to assign an alias to an imported class (similar to imported functions, discussed earlier). The chapter concludes with a brief discussions of code structuring, the Python standard library, and the naming and styling of classes that one writes.
File input/output and program exception handling are largely unrelated topics, and yet their treatments are combined into a single chapter. In my mind, it would have been more logical to cover exception handling and refactoring in the chapter that follows (which focuses on testing), and supplement the file input/output narrative with information on how to execute operating system commands within Python programs, read the results of those commands in one's code, and detect the error statuses of those commands. Nonetheless, the material presented by the author should be quite valuable to the budding Python programmer, since creating and reading files are an essential part of any high-level language, and exceptions are arguably the most robust technique for catching and processing all sorts of errors that can occur in one's programs, such as user input whose format does not match what has been requested or, even worse, may be an attempted attack on a web application's database server. The only portion of the narrative that seemed suspect to me was the claim that the
read() function adds an extra blank line – presumably this means a newline character – when it reaches the end of a file. This can be easily disproved with some code that creates and reads a zero-byte file:
f = open( 'empty_file', 'w' ); f.write( '' ); f.close(); f = open( 'empty_file' ); assert f.read() == ''. In fact, when I run the provided example program
file_reader.py, no extra blank line is appended to the output, unlike what is shown in the book's text. Regardless, the chapter ends strongly with coverage of two topics central to programming, especially for web applications that must save state: storing user data by utilizing methods made available in the JSON module, and improving one's code (and possibly that of others) through refactoring.
The final chapter of the first part of the book is dedicated to testing, a subject typically glossed over or wholeheartedly ignored in far too many programming language books. The author teaches the basics of unit tests utilizing the
unittest module, the test cases they compose, and the goal of full coverage — first for standalone functions and then object-oriented classes. The only blemish I found in the text is the statement that "Any method that starts with
test_ will be run automatically" (page 212, and reiterated on page 220). More correctly, that is true of any method that starts with
test without an underscore, although such should be added to be consistent with Python's function naming standards.
The second part of the book consists of three example projects: an alien invasion game, a data visualization program, and a web application. The first project — presented in chapters 12 through 14 — is a 2D game that relies heavily upon the Pygame package. Step by step, the author explains the various elements of the game and the corresponding chunks of code that make all of the gameplay possible. (As noted earlier, all of the sample code can be downloaded, examined, and run on one's own machine.) As intended, readers gain exposure to more complex Python code than what was seen in earlier chapters — including keyboard event handling and Pygame element control — and also witness the value of continual refactoring.
In the next three chapters, the author introduces data visualization – the graphical representation and study of numerical data and other quantifiable information using graphs, charts, maps, and similar forms — as well as data analysis. Using the Matplotlib mathematical plotting library, readers are shown how to start with a data set and then create a line graph, a scatter plot (with and without colormaps), and how to create a data set of random values in order to plot a random walk. Using the Plotly interactive visualization package, the author shows how to simulate thousands of rolls of a six-sided die and display the results graphically in web pages. One's data sets are not limited to random self-generated information, because there is a growing amount of data that can be freely downloaded from online sources, in many data formats (CSV and JSON are popular and are used in the book) and in many fields of study — in this case, variations in temperature in disparate cities, as well as the map locations and magnitudes of the most recent earthquakes worldwide. The example code demonstrates how to use colorscales, marker sizing, tooltips (mouse cursor hover text), and other techniques to better convey the information. The section ends with an introduction to automatically requesting specific data from remote websites — namely, information about Python projects on GitHub and articles on Hacker News — utilizing web application programming interfaces (APIs), again making use of the CSV and JSON data format standards.
In the final three chapters of the book, the author shows how one can build a web application (i.e., an interactive website with the capabilities of a desktop application) – specifically, an online journal system – using the Django web framework. By following the instructions in the text, any reader can theoretically set up a Python virtual environment to serve as a sandbox for developing the web app, install Django in it, create a new project in Django, create an SQLite database for the project to use, view the project page in a web browser (thereby verifying that Django has started the web server correctly), create a web app that does everything for the journal system except managing user accounts, and then startup the app by defining and activating a model. If any reader encounters difficulty in understanding the material in any part of this book, it will definitely be in this section, given the many settings that must be defined within the many files, all within a virtual environment that is not a standard file system, and sending its output not to the screen or a file but instead to a local web host, which likely could be the first local web host the reader has ever tried to work with. If an essential step were missed or an unusual error confronted, woe to the poor person trying to resolve the problem and get back on track! This material is the first place in the book where the reader who is trying to execute all the steps exactly as in the book, will need to copy files from the downloaded examples into the corresponding directories. One statement that deserved fuller explanation is "It's important to place your own apps before the default apps in case you need to override any behavior of the default apps with your own custom behavior" (page 387). At first glance, it seems to me that any overriding of behavior would need to be done after the default apps' behavior were established. The content of the remainder of Chapter 18 and the two that follow, won't be described here, because the material becomes increasingly complex, without adequate explanation as to how all the moving parts relate to one another — to the extent that anyone new to computer programming will likely find it all too obtuse and potentially overwhelming, and not worth the effort to try to slog through.
The book's four appendices cover various miscellaneous topics: the installation and troubleshooting of Python on Windows, macOS, and Linux (if necessary); Python reserved keywords and built-in functions; setting up the Sublime Text text editor; other available text editors (but no mention of the venerable jEdit or the superior Komodo IDE); resources and techniques for getting help; and how to control versions of your Python code using the popular open-source tool Git.
It is rather remarkable how such a lengthy technical book contains so few apparent errata, and minor ones at that. On page 159, "If this class was written" should instead read "If this class were written", as done correctly in the sentence before it. Some of the URLs in the text of the ebook (on pages xxxv, 7, 8, 13, 154, 181, 198, 202, 246, 324, 360, 365, 379, 381, 394, 406, 438, 448, 449, 458, 461, 469, 483) are not linked to their respective web pages.
One subject that I had expected to see addressed, but was not, was regular expressions — especially given their incredible versatility and power for matching and manipulating strings. Perhaps the author or publisher concluded that the book was already long enough and, given the slow pace of the narrative, regular expressions would have simply added too much additional material to the text. On the other hand, that material could in no way be more daunting than the Django- and SQLite-based web app that constitutes the final project. In everyday programming, which knowledge would be more useful to the new programmer? Regular expressions.
Interspersed throughout the text, wherever appropriate, the author provides helpful notes which typically warn the reader about potential pitfalls — for instance, in the chapter on lists, the unexpected behavior the reader might see when sorting a list in which the elements are not all in lowercase. One admirable feature of his example code snippets is that lines that were shown earlier are grayed out, making it easier for the reader to focus on the new concepts under discussion. The exercises sprinkled throughout the text can — if completed diligently by the reader — truly help them internalize the information being taught. Each one of the chapters has numerous exercises (each set titled "Try It Yourself") which encourage the reader to put into working code the ideas that they have just learned.
Because the book is intended to be accessible to people who have never programmed computers before, the narrative proceeds at a quite leisurely pace, with everything spelled out. This is ideal for raw beginners, but people experienced with at least one programming language may find the progress rather slow, even tedious. However, because the section and subsection headings generally explain their topics clearly, and the Python code naturally lends itself to clear expression, a non-beginner should not have any difficulties scanning the text at a rapid pace and absorbing all or at least more of the information. Perhaps the one place in the entire book where even the most greenhorn reader will conclude that the elementary style and slow pace have become excessive, is the sentence "Now we have a hamster named Harry and a dog named Willie" (page 133).
Aside from the Django death march mentioned earlier, the straightforward writing style of this monograph should prove very approachable for readers new to computer programming. At various points in the narrative, readers are gently encouraged to employ admirable coding practices, such as modularization of code into single-purpose functions, as well as assigning descriptive and easy-to-read names to the functions and modules they create.
With a patient and experienced pedagogical style, and a combination of thorough language instruction and plenty of illustrative sample code, Python Crash Course is a terrific way to begin learning computer programming in general and the Python language in particular.