Before the programming exercises for this part of the book, let’s run through some of the most common mistakes beginners make when coding Python statements and programs. Many of these are warnings I’ve thrown out earlier in this part of the book, collected here for ease of reference. You’ll learn to avoid these pitfalls once you’ve gained a bit of Python coding experience, but a few words now might help you avoid falling into some of these traps initially:
Don’t forget the colons. Always remember to type a : at the end of compound statement headers—the first line of an if, while, for, etc. You’ll probably forget at first (I did, and so have most of my roughly 4,000 Python students over the years), but you can take some comfort from the fact that it will soon become an unconscious habit.
Start in column 1. Be sure to start top-level (unnested) code in column 1. That includes unnested code typed into module files, as well as unnested code typed at the interactive prompt.
Blank lines matter at the interactive prompt. Blank lines in compound statements are always irrelevant and ignored in module files, but when you’re typing code at the interactive prompt, they end the statement. In other words, blank lines tell the interactive command line that you’ve finished a compound statement; if you want to continue, don’t hit the Enter key at the ... prompt (or in IDLE) until you’re really done. This also means you can’t paste multiline code at this prompt; it must run one full statement at a time.
Indent consistently. Avoid mixing tabs and spaces in the indentation of a block, unless you know what your text editor does with tabs. Otherwise, what you see in your editor may not be what Python sees when it counts tabs as a number of spaces. This is true in any block-structured language, not just Python—if the next programmer has tabs set differently, it will be difficult or impossible to understand the structure of your code. It’s safer to use all tabs or all spaces for each block.
Don’t code C in Python. A reminder for C/C++ programmers: you don’t need to type parentheses around tests in if and while headers (e.g., if (X==1):). You can, if you like (any expression can be enclosed in parentheses), but they are fully superfluous in this context. Also, do not terminate all your statements with semicolons; it’s technically legal to do this in Python as well, but it’s totally useless unless you’re placing more than one statement on a single line (the end of a line normally terminates a statement). And remember, don’t embed assignment statements in while loop tests, and don’t use {} around blocks (indent your nested code blocks consistently instead).
Use simple for loops instead of while or range. Another reminder: a simple for loop (e.g., for x in seq:) is almost always simpler to code and often quicker to run than a while- or range-based counter loop. Because Python handles indexing internally for a simple for, it can sometimes be faster than the equivalent while, though this can vary per code and Python. For code simplicity alone, though, avoid the temptation to count things in Python!
Beware of mutables in assignments. I mentioned this in Chapter 11: you need to be careful about using mutables in a multiple-target assignment (a = b = []), as well as in an augmented assignment (a += [1, 2]). In both cases, in-place changes may impact other variables. See Chapter 11 for details if you’ve forgotten why this is true.
Don’t expect results from functions that change objects in place. We encountered this one earlier, too: in-place change operations like the list.append and list.sort methods introduced in Chapter 8 do not return values (other than None), so you should call them without assigning the result. It’s not uncommon for beginners to say something like mylist = mylist.append(X) to try to get the result of an append, but what this actually does is assign mylist to None, not to the modified list (in fact, you’ll lose your reference to the list altogether).
A more devious example of this pops up in Python 2.X code when trying to step through dictionary items in a sorted fashion. It’s fairly common to see code like for k in D.keys().sort():. This almost works—the keys method builds a keys list, and the sort method orders it—but because the sort method returns None, the loop fails because it is ultimately a loop over None (a nonsequence). This fails even sooner in Python 3.X, because dictionary keys are views, not lists! To code this correctly, either use the newer sorted built-in function, which returns the sorted list, or split the method calls out to statements: Ks = list(D.keys()), then Ks.sort(), and finally, for k in Ks:. This, by the way, is one case where you may still want to call the keys method explicitly for looping, instead of relying on the dictionary iterators—iterators do not sort.
Always use parentheses to call a function. You must add parentheses after a function name to call it, whether it takes arguments or not (e.g., use function(), not function). In the next part of this book, we’ll learn that functions are simply objects that have a special operation—a call that you trigger with the parentheses. They can be referenced like any other object without triggering a call.
In classes, this problem seems to occur most often with files; it’s common to see beginners type file.close to close a file, rather than file.close(). Because it’s legal to reference a function without calling it, the first version with no parentheses succeeds silently, but it does not close the file!
Don’t use extensions or paths in imports and reloads. Omit directory paths and file extensions in import statements—say import mod, not import mod.py. We discussed module basics in Chapter 3 and will continue studying modules in Part V. Because modules may have other extensions besides .py (.pyc, for instance), hardcoding a particular extension is not only illegal syntax, it doesn’t make sense. Python picks an extension automatically, and any platform-specific directory path syntax comes from module search path settings, not the import statement.
And other pitfalls in other parts. Be sure to also see the built-in type warnings at the end of the prior part, as they may qualify as coding issues too. There are additional “gotchas” that crop up commonly in Python coding—losing a built-in function by reassigning its name, hiding a library module by using its name for one of your own, changing mutable argument defaults, and so on—but we don’t have enough background to cover them yet. To learn more about both what you should and shouldn’t do in Python, you’ll have to read on; later parts extend the set of “gotchas” and fixes we’ve added to here.