5 Python Pitfalls that can save you HOURS of debugging!
Knowing this can save you HOURS of debugging! (5 Python Pitfalls)
Being aware of these 5 Python Pitfalls can save you hours of debugging effort.
Credits go to the wtfpython repository!
1. String concatenation without "+"
Two strings are implicitly concatenated if they are written after each other, even without using the "+" operator. This can be relevant, e.g., if we have a list of strings and forget one comma.
Notice the missing comma in the list:
my_list = ["one",
"two"
"three",
"four",
"five"]
>>> len(my_list)
4
>>> my_list
["one", "twothree", "four", "five"]
Instead of raising a SyntaxError
, these strings are simply combined and we get unexpected results!
Implicit string concatenation:
a = "hello" "world"
>>> a
helloworld
2. Conditional expression and multiple assignments
In Python we can assign multiple variables in one line. Also, we can use a conditional expression instead of the longer if-else statement.
If both features are combined, we have to be really careful!
condition = False
x, y = (0, 1) if condition else None, None
>>> x, y
(None, None)
This is what we expected, but what happened if the condition is True?
condition = True
x, y = (0, 1) if condition else None, None
>>> x, y
((0, 1), None)
Now we get three values, what is happening???
Explanation: We tried to use parenthesis to improve the readability, but now we messed up the evaluation order! Now x will be (0, 1) if the condition is True, and None otherwise. And y is always None.
The Fix:
Either use consistent parenthesis for both cases:
condition = True
x, y = (0, 1) if condition else (None, None)
>>> x, y
(0, 1)
Or in this case I prefer the normal if-else statement, which is less error-prone:
if condition:
x, y = 0, 1
else:
x, y = None, None
3. Tuple creation with one element
Let's create tuples with different numbers of items, and inspect the items. As you know, tuples are created by using parenthesis ()
:
t = ('one', 'two')
for i in t:
print(i)
# Output:
one
two
This is what we expected for two elements.
Now create an empty tuple:
t = ()
for i in t:
print(i)
# Output:
No output, also what we expected.
Now create a tuple with one element:
t = ("one") # This is a string, not a tuple!!!
for i in t:
print(i)
# Output:
o
n
e
We print three items with one character each! What is happening???
Explanation: If we create a tuple with one element with parenthesis, the used syntax does not create a tuple but a string object!
The Fix:
A comma inside the parenthesis MUST be used to create a tuple with one element:
t = ("one",)
for i in t:
print(i)
# Output:
one
Another Pitfall
One could have the idea to use the explicit tuple constructor by using the tuple()
function. This will indeed create a tuple object, but when passing a string to this function, it creates a tuple with one character for each item:
t = tuple("one")
# ('o', 'n', 'e')
I have a detailed tutorial about tuples here.
4. assert
statement with parenthesis
The assert statement in Python is a convenient way to insert debugging assertions into a program:
If the condition is True, nothing happens, but if the condition is False, an AssertionError is raised (only in debug mode). This can be used to catch bugs when when we always expect a certain condition to be True.
The assert statement can take an optional Error message, separated by a comma:
a = "python"
b = "javascript"
>>> assert(a == b, "Both languages are different")
# No AssertionError is raised!!! But at least you get a warning:
<stdin>:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
Why is no AssertionError raised when the two variables are obviously not equal?
Explanation: The assert statement should NOT be used with parenthesis! Otherwise the whole statement is evaluated as one tuple with a Boolean and a string, and a non-empty tuple is always True.
Syntax according to the official docs is without parenthesis:
assert expression ["," errormsg]
The Fix:
a = "python"
b = "javascript"
>>> assert a == b, "Both languages are different"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: Both languages are different
5. In-place update methods of Lists/Dictionaries
This might be obvious, but sometimes we still forget it and then get unexpected None values.
Certain methods for Lists like append()
and sort()
modify the List in-place and return None! So when we assign the result of this operation back to the original variable, the object is None:
my_list = [1, 3, 2]
my_list = my_list.append(4)
# or
my_list = my_list.sort()
>>> my_list
None
The Fix:
Simply apply these methods without reassigning it to the variable:
my_list = [1, 3, 2]
my_list.append(4)
my_list.sort()
>>> my_list
[1, 2, 3, 4]
Same holds true for Dictionaries and the update()
function!