Alternate to global variable in Python - python

I am implementing a recursive function in which I need to remember a global value. I will decrement this value in every recursive call and want it to reflect in other recursive calls also.
Here's a way I've done it.
First way:
global a
a = 3
def foo():
global a
if a == 1:
print 1
return None
print a
a -= 1 # This new 'a' should be available in the next call to foo()
foo()
The output:
3
2
1
But I want to use another way because my professor says global variables are dangerous and one should avoid using them.
Also I am not simply passing the variable 'a' as argument because 'a' in my actual code is just to keep track of some numbers, that is to track the numbering of nodes I am visiting first to last. So, I don't want to make my program complex by introducing 'a' as argument in every call.
Please suggest me whatever is the best programming practice to solve the above problem.

Don't use a global; just make a a parameter to the function:
def foo(a):
print a
if a == 1:
return None
foo(a-1)
foo(3)

Try this :
Use a parameter instead of a global variable.
Example code
a = 3
def foo(param):
if param == 1:
print 1
return None
print param
foo(param - 1)
foo(a)

Related

Python while loop UnboundLocalError

I need a function with a while loop, that as long as the condition is False calls the function again.
This code will obviously result in a endless loop since "i" is always re declared as "0":
def fun():
i = 0
# does something else
while i < 5:
i += 1
fun()
print('done')
fun()
So I see no other way than to go about it like this:
i = 0
def fun():
# does something else
while i < 5:
i += 1
fun()
print('done')
fun()
But with this code It says local variable 'i' referenced before assignment.
I don't get it what am I doing wrong here?
I know I could use an if statement here, but that would be much more complicated and can get in my way in the project, although its not impossible.
Basically, the value i has not been declared in the function, so the function doesn't know what i is. There are a couple of ways to fix this.
One way is to put the while loop OUTSIDE of the function. This is how it would look like:
i = 0
def fun():
#Do Something...
print('done')
while i < 5:
i += 1
fun()
Another way would be to add the parameter i into the function. You do this by adding i into the brackets. This is how it would look like:
i = 0
def fun(i): #I Added An i Here So That The Function Knows We Need A Value Of i
#Do Something...
while i < 5:
i += 1
fun(i) #I Use The Same Value Of i Set At The Beginning
print('done')
fun(i) #I Call The Function Again
You could also set i as a global variable, meaning it can be used in the function too. To do that, just add global and the variable. This is how I did it:
i = 0
def fun():
global i
#Do Something...
while i < 5:
i += 1
fun()
print('done')
fun()
If neither of these 3 methods work, you could try and maybe use a for loop: something like for i in range(0, 5): , which would work too.
variable i is not in the scope of the function. Hence it is not accessible.
I believe the below code will help you get the desired output.
def fun(count):
# does something else
if(count >= 5):
return
fun(count + 1)
print('done')
fun(0)
Since other posts has offered remedy already, here I just point out the reason more detail:
This is one of the common gotchas to the new learners, the reason is that -
it's because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the 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.
It can be a surprise to get the UnboundLocalError in previously working code when it is modified by adding an assignment statement somewhere in the body of a function.
hope this is what you are looking for:
def fun(i=0):
# does something else
result = True
while result:
print(i)
i = i + 1
while i >= 5:
result = False
break
print('done')
fun()

Varialble re-initalized in multiple function calls

I have to execute the following code wherein I will be calling the function main again and again.
so here as I need to use i = i+1, I need to declare and initialize i in the first place right, but when i call the main function it again defines i=0 and the whole purpose of i = i+1 is lost.
How can I solve this error?
I have given the condition just as an example.
Basically what I want is i should be initialized only once, inspite of how many number of times main is called.
def main():
i = 0
if 0<1:
i = i+1
y = i
There are a couple ways to do this that don't involve globals. One is capture the value of i in a closure and return a new function that increments this. You will need to call the initial function once to get the returned function:
def main():
i = 0
def inner():
nonlocal i
i += 1
return i
return inner
f = main()
f()
# 1
f()
# 2
You can also create a generator which is a more pythonic way to do this. The generator can be iterated over (although use caution since it iterates forever) or you can get a single value by passing it to next():
def main():
i = 1
while True:
yield i
i += 1
f = main()
next(f)
# 1
next(f)
# 2
You can also use itertools.count
So you haven't declared i as a global variable
Do something like this
global i
i = 0
def main():
if 0<1:
global i
i = i+1
y = i
The reason behind this is because inside a function all the variables are local meaning they only exist inside the function while the function is called, so if you want a function to be able to change a variable for the whole code, you'll need to announce it as a global so python knows to change the value of it for the entire code
I'm not sure exactly what you are trying to do, but I believe there is an easier way to do whatever it is you are doing
It looks like you want to maintain state in a function call which is a good reason to convert it to a class.
class MyClass:
def __init__(self):
self.i = 0
def main(self):
self.i += 1
y = self.i
myclass = MyClass()
myclass.main()
myclass.main()
print(myclass.i)

Python Recursion with counter

I have a recursive function like this:
b=4
def someFunction(x,y,z):
global b
b += x
if...#something
else:
someFunction(x,y,z)
...
Theres a lot to it, but thats the general idea. My issue is that when running my program on cmd, I cant keep calling 'someFunction' with different parameters as there is no reset for b, however if I reset b within the function It won't work as I intend it to. So I could write this in two functions with the recursion in a sub function, but is there some other way to have b reset when I call the function without this being a problem when it starts recursion?
What I usually do is use an optional keyword argument, like this.
def someFunction(x,y,z, b=None):
if b is None:
b = 4
b += x
if...#something
else:
someFunction(x,y,z,b=b)
This way you can initialize the b on the first call to any value (and avoid usage of global variables).
This also has the positive that now someFunction is a pure function (no side effects and same input always gives same output) and much easier to work with. (Testing, refactoring, ..)
You can add an optional parameter of the function with the reset value:
def someFunction(x,y,z, reset = None):
if reset is not None:
b=reset
...
b += x
if...#something
else:
someFunction(x,y,z)
then the first time just call f(x,y,z, reset=4)

How do you create new commands in Python when immutable data types are involved? [duplicate]

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 5 years ago.
In Python, the following code prints '0', not '1'.
def inc(x):
x = x+1
a = 0
inc(a)
print(a)
I understand why this happens; it's because integers are immutable. What I don't understand is how to get around this behaviour when it's undesirable. Suppose we want to create a new command such that the code
a = 0
inc(a)
print(a)
prints '1'.
Obviously, the naive approach won't do it. What can we do instead?
Similar (a bit more general) question can be found here along with a discussion how Python passes params to functions. In short, without making x variable in your code an object, I believe there's nothing we can do. Of course, you can alter your code to e.g. return changed value from function inc() and print that (i.e. print(inc(x))) or just do the printing from inside the inc() method, but that's not what you're essentially looking for.
If I understand correctly, You are trying to increment variable a using function inc(var) and passing 'a' as a external variable to the function inc().
As #Marko Andrijevic stated, variable x passed to function inc() and variable x defined in the function are different . One way to achieve is by returning value of x and collecting externally, which you may not be looking for.
Alternately, Since you have defined variable 'a' outside function ,it can be called global variable.
If you want to pass that to a function, and manipulate it, you need to define that variable ('a' in your case) inside the function as global. Something like below.
def inc(x):
global a
a = x+1
Now when the new value assigned to 'a' after 'x+1', it is retained after execution of 'inc(x)'
>>> a = 0
>>> inc(a)
>>> a
1
EDIT -1
As per comments by #DYZ . Its correct. declaring global a inside inc() function will always increment a.
A better alternative will be , in that case, to return x inside inc() and assign that value to any external variable.
Not an elegant solution, but works as intended.
def inc(x):
return x+1
Result
>>> a
0
>>> a = inc(a)
>>> a
1
>>> a = inc(a)
>>> a
2
>>> b = 0
>>> b = inc(b)
>>> b
1
>>> a
2
>>>
one can use yield to get variable values.
def inc(x,y,z):
x += 1
y+=1
z+=1
yield x,y,z #inc doesn't stop
yield x+y+z
a=b=c=0
gen=inc(a,b,c)
gen=list(gen)
a,b,c,sum=gen[0]+(gen[1],) #however, index must still be known
print a,b,c,sum

Using a variable in a function in another function in python

I returned the variable and I still get the variable is still undefined. Can someone help?
def vote_percentage(s):
'''(string) = (float)
count the number of substrings 'yes' in
the string results and the number of substrings 'no' in the string
results, and it should return the percentage of "yes"
Precondition: String only contains yes, no, and abstained'''
s = s.lower()
s = s.strip()
yes = int(s.count("yes"))
no = int(s.count("no"))
percentage = yes / (no + yes)
return percentage
def vote(s):
##Calling function
vote_percentage(s)
if percentage == 1.0: ##problem runs here
print("The proposal passes unanimously.")
elif percentage >= (2/3) and percentage < 1.0:
print("The proposal passes with super majority.")
elif percentage < (2/3) and percentage >= .5:
print("The proposal passes with simple majority.")
else:
print("The proposal fails.")
Based on how you are implementing your code, if you define a variable in one method, you cannot access it in another.
The percentage variable inside vote_percentage is within scope of the vote_percentage method only, which means it cannot be used outside that method the way you are trying to use it.
So, in your vote_percentage you are returning percentage. Which means, that when you call this method, you need to actually assign its result to a variable.
So, to show you with an example using your code.
Looking at your code from here:
def vote(s):
##Calling function
vote_percentage(s)
What you need to be doing when calling vote_percentage is actually store the return value, so you can do something like this:
percentage = vote_percentage(s)
Now, you actually have the return of vote_percentage in the variable percentage.
Here is another small example to further explain scoping for you:
If you do this:
def foo()
x = "hello"
If you are outside the method foo(), you cannot access the variable x. It is within the "scope" of foo only. So if you do this:
def foo():
x = "hello"
return x
And you have another method that needs the result of foo(), you do not have access to that "x", so you need to store that return in a variable like this:
def boo():
x = foo()
As you can see in my example, similar to your code, I even used the variable x in boo(), because it is a "different" x. It is not within the same scope as foo().

Categories

Resources