5.3. Commonly Misused Syntax

If Python isn’t your first programming language, you might write your Python code with the same strategies you use to write code in other pro- gramming languages. Or perhaps you learned an unusual way of writing your Python code because you were unaware that there are more estab- lished best practices. This awkward code works, but you could save some time and effort by learning more standard approaches to writing pythonic code. This section explains common missteps programmers make and how you should write the code instead.

5.3.1. Use enumerate() instead of range()

When looping over a list or other sequence, some programmers use the range() and len() functions to generate the index integers from 0 up to, but not including, the length of the sequence. It’s common to use the variable name i (for index ) in these for loops. For example, enter the following unpythonic example into the interactive shell:

>>> animals = ['cat', 'dog', 'moose']
>>> for i in range(len(animals)):
>>>     print(i, animals[i])
0 cat
1 dog
2 moose

The range(len()) convention is straightforward but less than ideal because it can be difficult to read. Instead, pass the list or sequence to the built-in enumerate() function, which will return an integer for the index and the item at that index. For example, you can write the following pythonic code.

>>> animals = ['cat', 'dog', 'moose']
>>> for i, animal in enumerate(animals):
>>>     print(i, animal)
0 cat
1 dog
2 moose

The code you write will be slightly cleaner using enumerate() instead of range(len()) . If you need only the items but not the indexes, you can still directly iterate over the list in a pythonic way:

>>> animals = ['cat', 'dog', 'moose']
>>> for animal in animals:
>>>     print(animal)
cat
dog
moose

Calling enumerate() and iterating over a sequence directly are preferable to using the old-fashioned range(len()) convention.

5.3.2. Use the with Statement Instead of open() and close()

The open() function will return a file object that contains methods for reading or writing a file. When you’re done, the file object’s close() method makes the file available to other programs for reading and writing. You can use these functions individually. But doing so is unpythonic. For example, enter the following into the interactive shell to write the text “Hello, world!” to a file named spam.txt:

>>> # Unpythonic Example
>>> fileObj = open('spam.txt', 'w')
>>> fileObj.write('Hello, world!')
13
>>> fileObj.close()

Writing code this way can lead to unclosed files if, say, an error occurs in a try block and the program skips the call to close() . For example:

>>> # Unpythonic Example
>>> try:
>>>     fileObj = open('spam.txt', 'w')
>>>     eggs = 42 / 0# A zero divide error happens here.
>>>     fileObj.close() # This line never runs.
>>> except:
>>>     print('Some error occurred.')
Some error occurred.

Upon reaching the zero divide error, the execution moves to the except block, skipping the close() call and leaving the file open. This can lead to file corruption bugs later that are hard to trace back to the try block.

Instead, you can use the with statement to automatically call close() when the execution leaves the with statement’s block. The following pythonic example does the same task as the first example in this section:

>>> # Pythonic Example
>>> with open('spam.txt', 'w') as fileObj:
>>>     fileObj.write('Hello, world!')

Even though there’s no explicit call to close() , the with statement will know to call it when the execution leaves the block. .

5.3.3. Use is to Compare with None Instead of ==

The == equality operator compares two object’s values, whereas the is iden- tity operator compares two object’s identities. Chapter 7 covers value and identity. Two objects can store equivalent values, but being two separate objects means they have separate identities. However, whenever you com- pare a value to None , you should almost always use the is operator rather than the == operator.

In some cases, the expression spam == None could evaluate to True even when spam merely contains None . This can happen due to overloading the == operator, which Chapter 17 covers in more detail. But spam is None will check whether the value in the spam variable is literally None . Because None is the only value of the NoneType data type, there is only one None object in any Python program. If a variable is set to None , the is None comparison will always evaluate to True . Chapter 17 describes the specifics of overloading the == operator, but the following is an example of this behavior:

>>> class SomeClass:
>>>     def __eq__(self, other):
>>>         if other is None:
>>>             return True
>>>
>>> spam = SomeClass()
>>> spam == None
True
>>> spam is None
False

The possibility that a class overloads the == operator this way is rare, but it’s become idiomatic Python to always use is None instead of == None just in case.

Finally, you shouldn’t use the is operator with the values True and False . You can use the == equality operator to compare a value with True or False , such as spam == True or spam == False . Even more common is to leave out the operator and Boolean value altogether, writing code like if spam: or if not spam: instead of if spam == True: or if spam == False: .