Python global vs local variables? - python

I'm having trouble understanding why my code works the way it does. Right now, I'm initializing a global variable i set to 0, so it makes sense that if I print it anywhere outside my function, I should get 0.
When I print i inside the function, I get 6 and 12 after calling the function twice. I think this is because the global i is 0, but some local i variable isn't. However, when I'm calling reach_load with i as a parameter, aren't I passing in the global value of i (0)?
import sys
d = {}
size_0 = sys.getsizeof(d)
i = 0
def reach_load(d, size_0, i):
size_0 = sys.getsizeof(d)
while size_0 == sys.getsizeof(d):
d[i] = i
i += 1
print(i)
reach_load(d, size_0, i)
reach_load(d, size_0, i)

i is a purely local variable here. It is not linked to the global variable of the same name; the fact that you've called it the same thing makes no difference.

I think you've confused two things here: The global i doesn't change, but d does (as it's mutable). i starts off as 0 each time you call reach_load, but since the dictionary is bigger, the while loop will run longer and therefore, a higher number will be printed.

Because the i parameter to reach_load is a formal parameter it's local to the function. It's a local variable with the same label. If you really want to increment the global one, then put global i it the top of the function. However, this is considered bad design. If you need to keep some state define a new object with class to keep it.

When you call i in your function at line 10, in d[i], the Python interpreter you're using first checks for that variable in the local scope. If it doesn't find it, it then checks in the next scope, which in your case happens to be the global one. At this point, you are calling the global variable.
However, as soon as you do i += 1, i becomes a local variable since it's now defined in the local scope.
I'm not 100% on what you expect to happen, though. Are you wondering why the second run of the function returns different results?
If so, I believe your problem lies with your size_0 variable.
You define size_0 globally, but at the very start of your function, re-define it locally and that's the definition your function ends up using while the global size_0 ends up not being used at all. If you were to remove:
size_0 = sys.getsizeof(d)
from your function, each run would produce the same result.
What really helps to figure out these issues is adding various code that helps track the execution of your code. In this example, you could add a bunch of print() statements at critical points, such as print(d, size_0) # inside and outside the function.
It's difficult to give anymore advice as it's not clear to me what the code is supposed to accomplish.

Related

is it wrong to use same names for variables inside the function definition as thevariables used when calling a function in python

basically what the title says.
say i got a function definition
def add_one (num1):
numOut = num1 +1
return numOut
and im calling the function as follows
num1 = 11
numOut = add_one (num1)
is this considered bad practice, as we are naming two different objects with the same name, or is it good practice s it increases readability? Or is it something that I am missing completely?
(also checked pass variable to function using same name and Is it normal to use in the function some variable with the same name as it function? but they do not refer to python)
First, lets talk about scopes:
In python and a lot of other languages variables exist within its scope. This means that if you create a variable inside a scope it will stop existing when you leave that scope.
Variables on a outer scope are also accesible within an inner scope but if you asing something to that variable then it will be redeclared as a local variable inside this scope. Meaning that that variable will be diferent than the one on the outer scope.
If you ask me, I don't think is a bad practice to use the same name as the arguments of your functions. In fact I would call bad practice trying to avoid that as you would end up tryining to figure out new names for different variables that have the same meaning.
What you should avoid is to use variables that are not on the local scope (no matter what language are you using).
Try not to worry about that. Sometimes, it can make sense for argument variables to have the same name as the function's parameters, but you never want to enforce that. Your primary concern is that the code that defines a function should make sense regardless of how it might be called.
Remember that you might want to call a function passing some expression other than a plain variable, or that you might want to call the same function more than once in the same scope with different arguments. Trying to match the parameter names of a function you're calling only makes sense if you can't think of some better name for your variables.
When doing this there are always things to consider:
Am I overwriting the name of a variable I might need?
Am I overwriting the name of the function?
In the function's context, did the variable already exist?
However, in python the last point does not matter as outside variables (excluding globals) cannot be accessed within the function's scope.
In the following example, the name of the function is overwritten (which is bad):
def myFunc (a, b):
myFunc = 2
if a > 2 and a > b:
return a
return b
In this next example the context / scope of the function changes what variables you have access to in languages that are not python:
x = 3
def myFunc (a, b):
if a + x > b + x: # in some languages this would work, but in python this is an error because x is not defined in the function.
x = 5 # here x == 5, because we have redefined it. x is still 3 outside of our function though.
return a + x
return b
In the second example, using a variable's value before setting the value is regarded as bad practice, but you don't need to worry about that in python because it's not possible to do.
something that I am missing completely
Important feature of functions in python is their reusability, imagine changing requirementz force you to use your function
def add_one (num1):
numOut = num1 +1
return numOut
not for one, but for 2 different variables, you might end with
num1 = 11
num2 = 15
numOut = add_one (num1)
numOut2 = add_one (num2)
Now you have some variables which names are same like in function and same which are different. If you use different names from start not such problem occurs, for example:
x1 = 11
x2 = 15
x1out = add_one(x1)
x2out = add_one(x2)

python: check for variable redefinitions as loop variables

In python you can create rather hard to find bugs by giving a loop variable a name that already exists in the rest of your code.
The pattern looks like this:
idx = 0 # the result of some calculations
for idx in range(10):
# do something
# use idx and expect it to be 0
# surprise, idx is actually 9
Of course, the obvious solution is to have better variable names. But even if you use very descriptive names, there is still a chance that the same name seems like a good choice somewhere else in your code.
Especially in numerical code, there are just so many indices that it gets really hard to keep track. And variables pretty much never run out of scope:
for idx in range(10):
# do something
# idx is now in scope
# autocomplete will suggest idx, but it is still fine as a loop variable
for idx in range(10):
# do something
Is it possible to set up a linter for this problem ?
I want to be warned whenever I use a variable as a loop variable that has been defined in an enclosing scope. The rule would need a notion of scopes and their child-scopes. The first code sample above should create a warning. In the second example, the loop variable already exists in the outer scope, but it has been defined in a child of the enclosing scope. That should be fine.

Global and local in python function

I couldn't understand global and local variables in python, especially functions. I look lots of examples, but all of them don't explain in function calls clearly. My example code is below:
def called(d):
del d[0]
b=[1]
return b
def main():
a=[0,1,2,3]
print("first ", a)
c=called(a)
print("second ", a)
main()
Output:
first [0, 1, 2, 3]
second [1, 2, 3]
I expect that "a" is local variable at main. When I call the "called" function a is copyed to d. And d is local at "called". But reality is different. I solve the problem with a "called(a.copy)". But I want to understand the logic.
Python never implicitely copies anything, when you pass an object (and everything in Python is an object) to a function what you have in the function IS the object you passed in, so if you mutate it the change will be seen outside the function.
OTHO, parameters names are local to the function so rebinding a parameter within a function only change what object this name points to within the function, it has no impact on the object passed.
For more in-depth explanation the best reference is Ned Batchelder's article here.
Basically, a global variable is one that can be accessed anywhere, regardless of whether or not it is in a function (or anything else).
A local variable is one that solely exists within the function in question. You cannot declare or access it anywhere else. You could, however, make it global by explicitly including that in the function.
As far as I can see,'d' has not really been defined as a variable, but as a function parameter, as seen in called(d). This could've been changed to called(blabla) and your function would behave the same exact way if you, within the function, also changed d[0] to blabla[0].
What this means is that when you call that function, anything that has 'd' in it would be replaced by what you're calling it with. In this case, the parameter has been changed to the variable a, and the functions are then executed as you've stated.
In order to define a variable, you have to use '='.

What is the pythonic way to avoid shadowing variables?

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)
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)
PyCharm is going out of its way to prevent you from accidentally accessing(usually due to typos) a variable from the outer scope because doing so can create really nasty bugs.
Short Answer:
Most executables don't need to access the global state, so follow this structure that makes main a python function:
def helper_function():
# a function that will be used by main()
.
.
.
def main():
# make MAIN a function.
helper_function()
if __name__ == '__main__':
main()
# DONT define any variables here in this GLOBAL scope

Correctness about variable scope

I'm currently developing some things in Python and I have a question about variables scope.
This is the code:
a = None
anything = False
if anything:
a = 1
else:
a = 2
print a # prints 2
If I remove the first line (a = None) the code still works as before. However in this case I'd be declaring the variable inside an "if" block, and regarding other languages like Java, that variable would only be visible inside the "if".
How exactly variable scoping works in Python and what's the good way to program in cases like this?
Thanks!
As a rule of thumb, scopes are created in three places:
File-scope - otherwise known as module scope
Class-scope - created inside class blocks
Function-scope - created inside def blocks
(There are a few exceptions to these.)
Assigning to a name reserves it in the scope namespace, marked as unbound until reaching the first assignment. So for a mental model, you are assigning values to names in a scope.
I believe that Python uses function scope for local variables. That is, in any given function, if you assign a value to a local variable, it will be available from that moment onwards within that function until it returns. Therefore, since both branches of your code are guaranteed to assign to a, there is no need to assign None to a initially.
Note that when you can also access variables declared in outer functions -- in other words, Python has closures.
def adder(first):
def add(second):
return first + second
return add
This defines a function called adder. When called with an argument first, it will return a function that adds whatever argument it receives to first and return that value. For instance:
add_two = adder(2)
add_three = adder(3)
add_two(4) # = 6
add_three(4) # = 7
However, although you can read the value from the outer function, you can't change it (unlike in many other languages). For instance, imagine trying to implement an accumulator. You might write code like so:
def accumulator():
total = 0
def add(number):
total += number
return total
return add
Unfortunately, trying to use this code results in an error message:
UnboundLocalError: local variable 'total' referenced before assignment
This is because the line total += number tries to change the value of total, which cannot be done in this way in Python.
There is no problem assigning the variable in the if block.
In this case it is being assigned on both branches, so you can see it will definitely be defined when you come to print it.
If one of the branches did not assign to a then a NameError exception would be raise when you try to print it after that branch
Python doesn't need variables to be declared initially, so you can declare and define at arbitrary points. And yes, the scope is function scope, so it will be visible outside the if.
i'm quite a beginner programmer, but for what i know, in python private variables don't exist. see private variables in the python documentation for a detailed discussion.
useful informations can also be found in the section "scopes and namespaces" on the same page.
personally, i write code like the one you posted pretty much every day, especially when the condition relies in getting input from the user, for example
if len(sys.argv)==2:
f = open(sys.argv[1], 'r')
else:
print ('provide input file')
i do declare variables before using them for structured types, for example i declare an empty list before appending its items within a loop.
hope it helps.

Categories

Resources