Gotchas in Python - GeeksforGeeks

02 Sep, 2021
Python is a go-to language for most of the newcomers into the programming world. This is because it is fairly simple, highly in-demand, and ultimately powerful. But there are some cases which might confuse or rather trick a rookie coder. These are called “Gotchas”! Originating from the informal term “Got You!”, a gotcha is a case or scenario when the program plays the trick and results in an output that is quite different from what was expected. It should be noted that a gotcha is not an error or an exception. It is a perfectly valid code that results in an incorrect output only because we missed a teeny-tiny fact or point while writing our program. Therefore, we can also consider gotchas as “commonly made mistakes while coding”.
Let’s take a look at some most common gotchas in Python3 and how to tackle them:
The parenthesis gotchas :There are a few gotchas that arise when the parenthesis is used incorrectly or unnecessarily.Example:Python3Python3# results in Falseprint(5>2 == True) Output:FalseThis results in False because the above expression effectively means that 5>2 and 2==True. This implies, True and False. Hence, the final result is False.It can be corrected by the use of parenthesis.Python3Python3# results in Trueprint((5>2) == True)Output:True
Here is one more example:Python3Python3# results in Falseprint(5 is (not None)) Output:FalseThis is because "is not" is different from "is" and "not" being used separately. The part (not None) equals True and 5 is True results in False. It can be corrected by not using any parenthesis.Python3Python3# results in Trueprint(5 is not None)Output:True
“is”, “==”, “is not”, “!=” : This is a short but very common gotcha. Many new programmers often think that is and == are the same thing. But it’s definitely not!Python3Python3# results in Trueprint(1 == True)  # results in Falseprint(1 is True) Output:True
False
On the other hand, is not and != are same.Python3Python3# results in Trueprint(1 != False)   # results in Trueprint(1 is not False)Output:True
True
Default arguments gotcha :In Python, default arguments are declared only once when the function is run for the first time and from then on, the declared argument is used every time.Python3Python3def appendNew(appendTo =[]):          appendTo.append(1)       return appendTo      # Driver's codeprint(appendNew())print(appendNew())It’s expected that every time we call appendNew(), a new list will be created which will have 1 in it. But what happens is this:[1]
[1, 1]
The variable appendTo isn’t created again when the function is run for the second time. Instead, it’s created only the first time and used again and again. We can tackle it by:Python3Python3def appendNew(appendTo = None):      if appendTo == None:        appendTo =[]        appendTo.append(1)       return appendTo      # Driver's codeprint(appendNew())print(appendNew())Output:[1]
[1]
Scope gotchas :Sometimes, we must keep in mind the scope of the variable we are dealing with, i.e whether it is a global scope(works but inside and outside of a function) or a local scope(works just inside the function).Example:Python3Python3list1 = [1, 2, 3]def baz1():      # the code works fine    list1.append(4)     return list1def baz2():      # Doesn't work fine    list1 += [5]          return list1      # Driver's codeprint(baz1())print(baz2())Output:[1, 2, 3, 4]
Traceback (most recent call last):
  File "/home/ba0dfa25407638b061076b45ce165ce5.py", line 15, in 
    print(baz2())
  File "/home/ba0dfa25407638b061076b45ce165ce5.py", line 10, in baz2
    list1 += [5]      
UnboundLocalError: local variable 'list1' referenced before assignment
This happens becauselist1 += [5]means that we are assigning to the variable list1 but list1 is defined outside the scope of our function. While in baz1(), we are appending to list1 instead of assigning and hence it works fine.Variables are bound late in closures :Python has an infamous late binding behavior. By this, we mean that the value of a variable which is being iterated over is finalized to the value when it reaches its last iteration. Let’s look at an example:Python3Python3def create_multipliers():      # lambda function creates an iterable    # list anonymously    return [lambda c : i * c for i in range(6)]     for multiplier in create_multipliers():    print multiplier(3)The expected result is of course:0
3
6
9
12
15
But what we get is:15
15
15
15
15
15
This is because when the loop iteration is complete, i has a value of 5 and hence 3*5 each time results in 15.It can be solved by:Python3Python3def create_multipliers():    return [lambda x, i = i : i * x for i in range(6)]            for multiplier in create_multipliers():    print(multiplier(3))Output:0
3
6
9
12
15
Mutating a list while iterating over it :This is the most common gotcha which new coders face almost all the time. While working with a list or other mutable items, if we mutate it while iterating over it, it’s certain to cause errors. It’s recommended that we create the copy of the list instead and mutate it rather than the original list.Python3Python3# buggy program to print a list # of odd numbers from 1 to 10    even = lambda x : bool(x % 2)numbers = [n for n in range(10)]  for i in range(len(numbers)):    if not even(numbers[i]):        del numbers[i]Output:Traceback (most recent call last):
  File "/home/92eed8bfd8c92fca3cf85f22e8cfd9ea.py", line 9, in 
    if not even(numbers[i]):
IndexError: list index out of range
But if we use a copy of numbers instead:Python3Python3# working program to print a # list of odd numbers from 1 to 10    even = lambda x : bool(x % 2)numbers = [n for n in range(10)]  numbers[:] = [n for n in numbers if even(n)]print(numbers)  Output:[1, 3, 5, 7, 9]

The parenthesis gotchas :There are a few gotchas that arise when the parenthesis is used incorrectly or unnecessarily.Example:Python3Python3# results in Falseprint(5>2 == True) Output:FalseThis results in False because the above expression effectively means that 5>2 and 2==True. This implies, True and False. Hence, the final result is False.It can be corrected by the use of parenthesis.Python3Python3# results in Trueprint((5>2) == True)Output:True
Here is one more example:Python3Python3# results in Falseprint(5 is (not None)) Output:FalseThis is because "is not" is different from "is" and "not" being used separately. The part (not None) equals True and 5 is True results in False. It can be corrected by not using any parenthesis.Python3Python3# results in Trueprint(5 is not None)Output:True

There are a few gotchas that arise when the parenthesis is used incorrectly or unnecessarily.
Example:
Python3
# results in Falseprint(5>2 == True) 
Output:
False
This results in False because the above expression effectively means that 5>2 and 2==True. This implies, True and False. Hence, the final result is False.It can be corrected by the use of parenthesis.
Python3
# results in Trueprint((5>2) == True)
Output:
True

Here is one more example:
Python3
# results in Falseprint(5 is (not None)) 
Output:
False
This is because "is not" is different from "is" and "not" being used separately. The part (not None) equals True and 5 is True results in False. It can be corrected by not using any parenthesis.
Python3
# results in Trueprint(5 is not None)
Output:
True

“is”, “==”, “is not”, “!=” : This is a short but very common gotcha. Many new programmers often think that is and == are the same thing. But it’s definitely not!Python3Python3# results in Trueprint(1 == True)  # results in Falseprint(1 is True) Output:True
False
On the other hand, is not and != are same.Python3Python3# results in Trueprint(1 != False)   # results in Trueprint(1 is not False)Output:True
True

Python3
# results in Trueprint(1 == True)  # results in Falseprint(1 is True) 
Output:
True
False

On the other hand, is not and != are same.
Python3
# results in Trueprint(1 != False)   # results in Trueprint(1 is not False)
Output:
True
True

Default arguments gotcha :In Python, default arguments are declared only once when the function is run for the first time and from then on, the declared argument is used every time.Python3Python3def appendNew(appendTo =[]):          appendTo.append(1)       return appendTo      # Driver's codeprint(appendNew())print(appendNew())It’s expected that every time we call appendNew(), a new list will be created which will have 1 in it. But what happens is this:[1]
[1, 1]
The variable appendTo isn’t created again when the function is run for the second time. Instead, it’s created only the first time and used again and again. We can tackle it by:Python3Python3def appendNew(appendTo = None):      if appendTo == None:        appendTo =[]        appendTo.append(1)       return appendTo      # Driver's codeprint(appendNew())print(appendNew())Output:[1]
[1]

In Python, default arguments are declared only once when the function is run for the first time and from then on, the declared argument is used every time.
Python3
def appendNew(appendTo =[]):          appendTo.append(1)       return appendTo      # Driver's codeprint(appendNew())print(appendNew())
It’s expected that every time we call appendNew(), a new list will be created which will have 1 in it. But what happens is this:
[1]
[1, 1]

The variable appendTo isn’t created again when the function is run for the second time. Instead, it’s created only the first time and used again and again. We can tackle it by:
Python3
def appendNew(appendTo = None):      if appendTo == None:        appendTo =[]        appendTo.append(1)       return appendTo      # Driver's codeprint(appendNew())print(appendNew())
Output:
[1]
[1]

Scope gotchas :Sometimes, we must keep in mind the scope of the variable we are dealing with, i.e whether it is a global scope(works but inside and outside of a function) or a local scope(works just inside the function).Example:Python3Python3list1 = [1, 2, 3]def baz1():      # the code works fine    list1.append(4)     return list1def baz2():      # Doesn't work fine    list1 += [5]          return list1      # Driver's codeprint(baz1())print(baz2())Output:[1, 2, 3, 4]
Traceback (most recent call last):
  File "/home/ba0dfa25407638b061076b45ce165ce5.py", line 15, in 
    print(baz2())
  File "/home/ba0dfa25407638b061076b45ce165ce5.py", line 10, in baz2
    list1 += [5]      
UnboundLocalError: local variable 'list1' referenced before assignment
This happens becauselist1 += [5]means that we are assigning to the variable list1 but list1 is defined outside the scope of our function. While in baz1(), we are appending to list1 instead of assigning and hence it works fine.
Sometimes, we must keep in mind the scope of the variable we are dealing with, i.e whether it is a global scope(works but inside and outside of a function) or a local scope(works just inside the function).
Example:
Python3
list1 = [1, 2, 3]def baz1():      # the code works fine    list1.append(4)     return list1def baz2():      # Doesn't work fine    list1 += [5]          return list1      # Driver's codeprint(baz1())print(baz2())
Output:
[1, 2, 3, 4]

Traceback (most recent call last):
  File "/home/ba0dfa25407638b061076b45ce165ce5.py", line 15, in 
    print(baz2())
  File "/home/ba0dfa25407638b061076b45ce165ce5.py", line 10, in baz2
    list1 += [5]      
UnboundLocalError: local variable 'list1' referenced before assignment

This happens because
list1 += [5]
means that we are assigning to the variable list1 but list1 is defined outside the scope of our function. While in baz1(), we are appending to list1 instead of assigning and hence it works fine.
Variables are bound late in closures :Python has an infamous late binding behavior. By this, we mean that the value of a variable which is being iterated over is finalized to the value when it reaches its last iteration. Let’s look at an example:Python3Python3def create_multipliers():      # lambda function creates an iterable    # list anonymously    return [lambda c : i * c for i in range(6)]     for multiplier in create_multipliers():    print multiplier(3)The expected result is of course:0
3
6
9
12
15
But what we get is:15
15
15
15
15
15
This is because when the loop iteration is complete, i has a value of 5 and hence 3*5 each time results in 15.It can be solved by:Python3Python3def create_multipliers():    return [lambda x, i = i : i * x for i in range(6)]            for multiplier in create_multipliers():    print(multiplier(3))Output:0
3
6
9
12
15

Python has an infamous late binding behavior. By this, we mean that the value of a variable which is being iterated over is finalized to the value when it reaches its last iteration. Let’s look at an example:
Python3
def create_multipliers():      # lambda function creates an iterable    # list anonymously    return [lambda c : i * c for i in range(6)]     for multiplier in create_multipliers():    print multiplier(3)
The expected result is of course:
0
3
6
9
12
15

But what we get is:
15
15
15
15
15
15

This is because when the loop iteration is complete, i has a value of 5 and hence 3*5 each time results in 15.It can be solved by:
Python3
def create_multipliers():    return [lambda x, i = i : i * x for i in range(6)]            for multiplier in create_multipliers():    print(multiplier(3))
Output:
0
3
6
9
12
15

Mutating a list while iterating over it :This is the most common gotcha which new coders face almost all the time. While working with a list or other mutable items, if we mutate it while iterating over it, it’s certain to cause errors. It’s recommended that we create the copy of the list instead and mutate it rather than the original list.Python3Python3# buggy program to print a list # of odd numbers from 1 to 10    even = lambda x : bool(x % 2)numbers = [n for n in range(10)]  for i in range(len(numbers)):    if not even(numbers[i]):        del numbers[i]Output:Traceback (most recent call last):
  File "/home/92eed8bfd8c92fca3cf85f22e8cfd9ea.py", line 9, in 
    if not even(numbers[i]):
IndexError: list index out of range
But if we use a copy of numbers instead:Python3Python3# working program to print a # list of odd numbers from 1 to 10    even = lambda x : bool(x % 2)numbers = [n for n in range(10)]  numbers[:] = [n for n in numbers if even(n)]print(numbers)  Output:[1, 3, 5, 7, 9]

This is the most common gotcha which new coders face almost all the time. While working with a list or other mutable items, if we mutate it while iterating over it, it’s certain to cause errors. It’s recommended that we create the copy of the list instead and mutate it rather than the original list.
Python3
# buggy program to print a list # of odd numbers from 1 to 10    even = lambda x : bool(x % 2)numbers = [n for n in range(10)]  for i in range(len(numbers)):    if not even(numbers[i]):        del numbers[i]
Output:
Traceback (most recent call last):
  File "/home/92eed8bfd8c92fca3cf85f22e8cfd9ea.py", line 9, in 
    if not even(numbers[i]):
IndexError: list index out of range

But if we use a copy of numbers instead:
Python3
# working program to print a # list of odd numbers from 1 to 10    even = lambda x : bool(x % 2)numbers = [n for n in range(10)]  numbers[:] = [n for n in numbers if even(n)]print(numbers)  
Output:
[1, 3, 5, 7, 9]

abhishek0719kadiyan
python-utility
Python
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
How to Install PIP on Windows ?
Check if element exists in list in Python
How To Convert Python Dictionary To JSON?
Python Classes and Objects
How to drop one or multiple columns in Pandas Dataframe
Defaultdict in Python
Python | Get unique values from a list
Python | os.path.join() method
Create a directory in Python
Python | Pandas dataframe.groupby()