Misunderstanding Python scopes
Python scope resolution is based on what is known as the LEGB rule, which is shorthand for Local, Enclosing, Global, Built-in. Even though it looks pretty simple, it’s was very confusing for me at the time. So, let’s have a look at the example below:
For the code above I’d have expected it to work, and altering the global variable
x to finally printing
6. But, it can get weirder, let’s look at the following altered code:
What in the world is going on? In one code snippet, the global variable
UnboundLocalError. However, when we try to print the variable, it works. The has to do with scoping. When you make an assignment to a variable in a scope (e.g. the function scope), that variable becomes local to that scope and shadows any similarly named variable in the outer scope. This is what happened in the first scenario when we did
x += 1.
If what we are intending is to access the global variable
xas in the case of our function
foo(), we could do something like this:
By using the keyword
global, it allows the inner scope to access the variable declared in the global scope, meaning variables that are not defined in any function. Similarly, we could use
nonlocalto produce a similar effect:
globalallows us to access variables from an outer scope, however, in the case of
nonlocal, you can bound an object on a parent scope or the global scope.
Modifying a list while iterating over it
Though this error is not only common to Python, it’s an error commonly spotted among new Python developers, and even to some experienced developers. Though sometimes it may not seem so obvious, under certain occasions we end up modifying the array we’re currently iterating resulting in unappropriated behavior. Or if we are lucky, we get an error and easily notice it.
But let me give you an example of what I mean. Let’ say that given an array you need to reduce that array to contain only the even elements, you may attempt to do something like:
In the scenario described, when deleting an element for a list or array while iterating, we get an error as we try to access an item that is not there anymore. This is bad practice and should be avoided, there are better ways to achieve similar things in Python, among them are list comprehensions:
You could also use the
filterfunction to achieve the same thing. Though it works, some argue this is not the Pythonic way of doing it, and I kind of agree. But I don’t want to get into the middle of that discussion, I’d rather give you the options, and you can research and decide:
Variable binding in closures
I’d like to start with a quiz I posted on twitter (@livecodestream) where I asked people about what they think the result of the following snippet would be:
For many people, myself included, the first time we encounter this problem we think that the result is:
However, the code actually resulted in something totally different and I was very puzzled at this. What is actually happening is that Python would do a late-binding behavior, according to which the values of variables used in closures are looked up at the time the inner function is called. So in our example, whenever any of the returned functions are called, the value of
i is looked up in the surrounding scope at the time it is called.
A solution to this problem may seem a bit ‘hacky,’ but it actually works:
By using the default argument of the lambda function to pass the value of
i , we can generate the functions to do the desired behavior. I was very puzzled by this solution, and I still consider it not very elegant, however, some people love it.
Name clashing with Python Standard Library modules
This issue was actually pretty common when first started working with Python. Even today, I sometimes make this mistake. The issue comes as a result of naming one of your modules with the same name as a module in the standard library that ships with Python. For example, you might have a module named email.py in your code, which would be in conflict with the standard library module of the same name.
Perhaps the name clashing by itself won’t generate any issues with your code, but sometimes we override a function or module of the Python standard library, which is later used in an installed library, and it conflicts either by throwing errors or misbehaving. In any case, it’s a bad situation to have.
A classic mistake is the following:
By simply creating a variable named
list we broke the access to the
list function. Even though there are other ways of accessing it (e.g.
__builtins__.list()), we should avoid this kind of name.
This article does not cover all the common mistakes developers do when coding in Python, but rather those things I struggled the most. If you want to know more about how to write great Python code and avoiding some other mistakes I recommend you to read Make Your Code Great, Python Style.
Live Code Stream is also available as a free weekly newsletter. Sign up for updates on everything related to programming, AI, and computer science in general.