What is the problem with shadowing names defined in outer scopes?

PythonPycharmShadowing

Python Problem Overview


I just switched to PyCharm and I am very happy about all the warnings and hints it provides me to improve my code. Except for this one which I don't understand:

> This inspection detects shadowing names defined in outer scopes.

I know it is bad practice to access variable from the outer scope, but what is the problem with shadowing the outer scope?

Here is one example, where PyCharm gives me the warning message:

data = [4, 5, 6]

def print_data(data): # <-- Warning: "Shadows 'data' from outer scope
    print data

print_data(data)

Python Solutions


Solution 1 - Python

There isn't any big deal in your above snippet, but imagine a function with a few more arguments and quite a few more lines of code. Then you decide to rename your data argument as yadda, but miss one of the places it is used in the function's body... Now data refers to the global, and you start having weird behaviour - where you would have a much more obvious NameError if you didn't have a global name data.

Also remember that in Python everything is an object (including modules, classes and functions), so there's no distinct namespaces for functions, modules or classes. Another scenario is that you import function foo at the top of your module, and use it somewhere in your function body. Then you add a new argument to your function and named it - bad luck - foo.

Finally, built-in functions and types also live in the same namespace and can be shadowed the same way.

None of this is much of a problem if you have short functions, good naming and a decent unit test coverage, but well, sometimes you have to maintain less than perfect code and being warned about such possible issues might help.

Solution 2 - Python

The currently most up-voted and accepted answer and most answers here miss the point.

It doesn't matter how long your function is, or how you name your variable descriptively (to hopefully minimize the chance of potential name collision).

The fact that your function's local variable or its parameter happens to share a name in the global scope is completely irrelevant. And in fact, no matter how carefully you choose you local variable name, your function can never foresee "whether my cool name yadda will also be used as a global variable in future?". The solution? Simply don't worry about that! The correct mindset is to design your function to consume input from and only from its parameters in signature. That way you don't need to care what is (or will be) in global scope, and then shadowing becomes not an issue at all.

In other words, the shadowing problem only matters when your function need to use the same name local variable and the global variable. But you should avoid such design in the first place. The OP's code does not really have such design problem. It is just that PyCharm is not smart enough and it gives out a warning just in case. So, just to make PyCharm happy, and also make our code clean, see this solution quoting from silyevsk's answer to remove the global variable completely.

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()

This is the proper way to "solve" this problem, by fixing/removing your global thing, not adjusting your current local function.

Solution 3 - Python

A good workaround in some cases may be to move the variables and code to another function:

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()

Solution 4 - Python

I like to see a green tick in the top right corner in PyCharm. I append the variable names with an underscore just to clear this warning so I can focus on the important warnings.

data = [4, 5, 6]

def print_data(data_):
    print(data_)

print_data(data)

Solution 5 - Python

It depends how long the function is. The longer the function, the greater the chance that someone modifying it in future will write data thinking that it means the global. In fact, it means the local, but because the function is so long, it's not obvious to them that there exists a local with that name.

For your example function, I think that shadowing the global is not bad at all.

Solution 6 - Python

Do this:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()

Solution 7 - Python

data = [4, 5, 6] # Your global variable

def print_data(data): # <-- Pass in a parameter called "data"
    print data  # <-- Note: You can access global variable inside your function, BUT for now, which is which? the parameter or the global variable? Confused, huh?

print_data(data)

Solution 8 - Python

It looks like it is 100% a pytest code pattern.

See:

pytest fixtures: explicit, modular, scalable

I had the same problem with it, and this is why I found this post ;)

# ./tests/test_twitter1.py
import os
import pytest

from mylib import db
# ...

@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

And it will warn with This inspection detects shadowing names defined in outer scopes.

To fix that, just move your twitter fixture into ./tests/conftest.py

# ./tests/conftest.py
import pytest

from syntropy import db


@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

And remove the twitter fixture, like in ./tests/test_twitter2.py:

# ./tests/test_twitter2.py
import os
import pytest

from mylib import db
# ...

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

This will be make happy for QA, PyCharm and everyone.

Solution 9 - Python

To ignore the warning, as Chistopher said in a comment, you can comment above it

# noinspection PyShadowingNames

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFramesterView Question on Stackoverflow
Solution 1 - Pythonbruno desthuilliersView Answer on Stackoverflow
Solution 2 - PythonRayLuoView Answer on Stackoverflow
Solution 3 - PythonsilyevskView Answer on Stackoverflow
Solution 4 - PythonBazView Answer on Stackoverflow
Solution 5 - PythonSteve JessopView Answer on Stackoverflow
Solution 6 - Pythonuser8312800View Answer on Stackoverflow
Solution 7 - PythonJoeCView Answer on Stackoverflow
Solution 8 - PythonAndrei.DanciucView Answer on Stackoverflow
Solution 9 - PythonJoshua WolffView Answer on Stackoverflow