I often have the following code which either leads to variable shadowing or to a multiplication of local variables
def whenadult(age): return 18 - age age = 5 needtowait = whenadult(age)
age
has the same logical role both when passed to the function as in the main code so I would like to avoid creating something like l_age
in whenadult.
What is the pythonic way to solve the "shadowing vs. variable multiplication" dilemma?
UPDATE: following up on some comments I want to make it clear that I was looking for a Python best practice (as opposed to local vs. global variables scope)
Variable shadowing could be avoided by simply renaming variables with unambiguous names. We could rewrite the previous examples: The inner scope has access to variables defined in the outer scope.
In computer programming, variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) has the same name as a variable declared in an outer scope.
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function's body, it's assumed to be a local unless explicitly declared as global.
Variable shadowing occurs when a variable defined in the inner scope has the same name as a variable in the outer scope. Consider the example below, here the variable inside the function has the same name as a global variable.
The fact that the local variable (and function parameter) age
happens to have the same name as a variable somewhere else in your program is irrelevant. The whole point of local variables is that they only live within the local scope of the function they're defined in.
The fact that the local variable has the same name as the variable used elsewhere as an argument is especially not a problem. In fact, it's very common in real-life code. For example, picking a random stdlib module, the 3.3 version of cmd
, the Cmd.onecmd
method has a variable named line
, and it passes it as an argument to the self.default
method, which binds it to a parameter that's also named line
.
The fact that the variable used for the argument happens to be a global variable that you could have accessed, if you didn't have a local variable of the same name, is not a problem unless you actually wanted to access that global variable. Which you didn't want to in your existing code, and almost never should want to. In this case, and in most real-world cases, it's simply a coincidence that means nothing and affects nothing, not a problem you have to solve.
The problem you're having is that PyCharm can't guess whether you wanted the global age
to be accessible in whenadult
. Is it possible (if not in this trivial case, maybe in more complex cases) that a human might be similarly confused, slowing down his comprehension of your code? Or that you'll one day have to write code in some environment where your code reviewers or teacher or whatever will reject your code because it doesn't pass some linter with no warnings? Maybe.
But really, in any such environment, they'd probably complain about you using global variables in the first place. And you really don't need to here. The only reason age
is a global is that it has to be accessible to the top-level code. If you move that code into a function, age
can become a local in that function. For example:
def whenadult(age): return 18 - age def main(): age = 5 needtowait = whenadult(age) main() # possibly with an if __name__ == '__main__' guard
This will make PyCharm happy, and any linter tools, and any easily-confused or rigidly-minded human readers. It'll even make your code a tiny bit faster. On the other hand, it's more code to read—only three lines and one indent, but then the whole program is only eight lines long. So, it's a tradeoff that you can make on a case-by-case basis.
Whenever I got the warning of shadowing variable in PyCharm. I would try to rename the local variable to use the underscore prefix as the convention. That's another way to consider in addition to wrap global variables into a main() function.
def whenadult(_age): return 18 - _age age = 5 needtowait = whenadult(age)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With