Varialble re-initalized in multiple function calls - python

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)

Related

Get a Variable directly from function Python

i want to work with the variable "sets" after the function has been called.
How can i do this?
sets = 0
def search_str(monatsabrechnung, d1):
with open(monatsabrechnung, 'r') as file:
content = file.read()
if lastDay == heute and ja == 1:
sets = 1
else:
pass
search_str(monatsabrechnung, d1)
print(sets)
IIUC, you are trying to modify a variable in a function, which is originally defined outside a function. This is a variable scoping problem. Do check this awesome article to get an understanding of how variable scopes work in python.
Back to your code, the issue here is that even though you run the function to modify the variable sets to 1, python goes back to the outer scope where sets were set to 0.
sets = 0 #<-- outer scope
def search_str():
... #do something
sets = 1 #<-- inner scope
search_str()
print(sets) #back to outer scope
# 0
Solution 1: Pass as a parameter and return
You will have to pass the variable as a parameter to the function and then return it as follows -
sets = 0
def search_str(sets): #<-- pass as paramter
... #do something
sets = 1 #<-- modify
return sets
output = search_str(sets) #<-- save the output
print(output)
# 1
TIP: If your function is already returning another output, you can actually return, store and unpack multiple return values at once -
In your return statement, return everything you need return x, y, sets
Then, while calling use - X, Y, sets = search_str(...)
Solution 2: Set scope to global
If passing a parameter and returning is not an option for you, the scope of the variable has to be made global -
sets = 0
def search_str():
global sets #<-- set the scope to global
... #do something
sets = 1
search_str()
print(sets)
# 1
EDIT: As an additional pointer, as Matthias points out in his comments correctly, global scoping is usually avoided as it can cause a lot of problems if not careful.
Here is a great StackOverflow thread detailing Why are global variables evil?
Return the new value of sets from the function. If the if statement is true, then return 1, otherwise return the same value as before. To be able to return the old value you should add it as a parameter to the function.
sets = 0
def search_str(monatsabrechnung, d1, sets):
with open(monatsabrechnung, 'r') as file:
content = file.read()
if lastDay == heute and ja == 1:
current_sets = 1
else:
current_sets = sets
return current_sets
sets = search_str(monatsabrechnung, d1, sets)
Don't use global if you don't have to. You get side-effects and the code is less reusable.

Python function argument can't be used as a variable in a child function

Here's the problem I try to solve:
I have a first function, to which I put in arguments. Then, later on, I have a second function, from which I want to call, as a variable, the said argument of the parent function. So it goes like:
def parent_function(argument=x):
if statement:
child_function()
else:
...
return result
def child_function():
x = x + 5
return x
If I run such a code, I get an error in the child function saying name 'x' is not defined.
However, if I fix my code to make x global in the parent function, like this:
def parent_function(argument=x):
global x
if statement:
child_function()
else:
...
return result
def child_function():
x = x + 5
return x
I get the error name 'x' is parameter and global
I need to import both functions in another file and I can't "dismantle" the child function inside the parent function.
Thanks very much for any help !
Don't use global Variables. Every function needs it's own arguments:
def parent_function(x):
if statement:
x = child_function(x)
else:
...
return result
def child_function(x):
x = x + 5
return x
name 'x' is parameter and global means you can't overwrite parameter x for being global also. To fix this, use another variable y, like this:
def parent_function(argument=x):
global y
y = x
if statement:
child_function()
else:
...
return result
def child_function():
y = y + 5
return y
This error happens because you are trying to overwrite a parameter in a function whose scope is local to that function by giving it a global scope. The problem is that variables defined in the context of a function are by definition local variables. To better illustrate this problem ,you can simply try to launch this piece of code:
def parent_function(argument="Hello"):
global argument
return argument
You will see that it will fail to run for the same reason that I have explained. I hope I have been clear in my explanation. Good luck.
The first thing you need to change is this:
def parent_function(argument=x):
If you search for how to make a default argument in a function you will get something like this: https://www.geeksforgeeks.org/default-arguments-in-python/
. This means instead of x you need to have some default value, for example:
def parent_function(argument=5):
This means that if you do not pass the argument called argument to the function value 5 will be passed.
On the other hand, it seems that you want x to be an argument, which means the def line should look like this:
def parent_function(x=5):
Second, global keyword needs to be used in the child_function since x has not been used in parent_function. This leads to this:
def parent_function(x=5):
if statement:
child_function()
else:
...
return result
def child_function():
global x
x = x + 5
return x
To have all this work, there must be at least two more lines one to set x and another to call parent_function, like this:
x = 6
parent_function(4)
But, to be even funnier, x from the arguments in parent_function and x used in child_function are not the same thing, and you can see for yourself in this example which is similar to your code, but fully executable:
def parent_function(x=5):
if True:
print(child_function())
else:
print("else branch")
return True
def child_function():
global x
x = x + 5
return x
x = 6
parent_function(4)
This prints out 11 even you might think it will print out 9!
This is due to fact that keyword global refers to the (as the word says) global variable declared outside of the functions, the variable with value 6. Usually, local and global variables should have different names, so either the argument x in parent_function or global x variable needs to be renamed.
IDK if this helps, but you will learn something from this, for sure!

List of objects function not working

Sorry for the title, I hope it reflects correctly my problem :
In the following code, I was expecting the result to be result 0 1 2 but instead I have 2 2 2. The code inside my_function seems to be interpreted with the last instance of obj. What is wrong ?
class Example:
def __init__(self, x):
self.x = x
def get(self):
return self.x
a_list = []
for index in range(3):
obj = Example(index)
def my_function(x):
#some stuff with x like obj.another_function(x)
return obj.get()
a_list.append(my_function)
for c in a_list:
print(c())
When you define this
def my_function():
return obj.get()
Python will understand that my_function should run the get() method of an object called obj and return the value. It won't know the value of obj and what the get() method does until you attempt to call it.
So, you are actually defining three different functions that will eventually do the same thing. And, in the end, running the same code thrice.
But why is the return 2 2 2?
Because after the last iteration, the value of obj is Example(2)* because you redefine its value at every iteration, and the last one remains.
*
because of this line obj = Example(index)
Understanding a few things about how python works will help you understand what's happening here. Here obj is a closure, closures are evaluated at call time, not when the function is defined so if I do this:
x = "hello"
def printX():
print x
x = "goodbye"
printX() # goodbye
I get "goodbye" because printX is referencing a global variable in my module, which changes after I create printX.
What you want to do is create a function with a closure that references a specific object. The functional way to do this is to create a function that returns another function:
x = "hello"
def makePrintX(a):
def printX():
# We print a, the object passed to `makePrintX`
print a
return printX
# x is evaluated here when it is still "hello"
myPrintX = makePrintX(x)
x = "goodbye"
myPrintX() # "hello"
If you're having trouble understanding the above example I would recommend reading up on python's scoping rules. For your example, you could do something like this:
class Example:
def __init__(self, x):
self.x = x
def get(self):
return self.x
def makeObjFunction(obj):
def objFunction(x):
return obj.get()
return objFunction
a_list = []
for index in range(3):
obj = Example(index)
my_function = makeObjFunction(obj)
a_list.append(my_function)
for c in a_list:
print(c("some value"))
You are appending three my_functions to the a_list which are all closures over the same Example object. Try:
def my_function():
return obj
<__main__.Example object at 0x0054EDF0>
<__main__.Example object at 0x0054EDF0>
<__main__.Example object at 0x0054EDF0>
You can see they have the same id so calling get() on each should give the same answer.
If you just append the obj.get function (and drop the my_function) it'll work fine.
a_list.append(obj.get)
....
0
1
2
Edit: You've updated your question so to let you do more stuff in my_function(). It's still basically a scoping problem.
def my_func_factory(p_obj):
def my_function(x):
#some stuff with x like obj.another_function(x)
return p_obj.get()
return my_function
for index in range(3):
obj = Example(index)
a_list.append(my_func_factory(obj))
Since my_function can't see obj being reassigned, each instance doesn't pick up the change.
I think append() during the for just append the function address in a_list[]. After for iteration, the a_list is really given the number. Then it discovers the address of my_function, and they get the number in my_function, this is, 2. That's why you get [2,2,2].
Or maybe, in my_function, function give the method of "obj". But for iteration change the "obj" memory address each time, so the symbol "obj" always aim to the newest object Example. Due to my_function always get "obj", you get the same number from the last object.

Alternate to global variable in 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)

How to use local variable in a function and return it?

I am trying to create a script that sets a local variable, references it from a function, and can return the manipulated value back to the main scope (or whatever it's called; I'm new to Python)
I have simplified my code to show the utmost basics of what I am trying to accomplish, which is to import a local from the module into a function block, I think.
I have gotten this to work by using globals, but that isn't the best solution . . .
chambersinreactor = 0;
cardsdiscarded = 0;
def find_chamber_discard():
"""Find chambers and discard in row (reads each player slot)"""
chambersinreactor = 0; # Resets the variable, not what I want
cardsdiscarded = 0; # Resets the variable, not what I want
chambersinreactor += 1
cardsdiscarded += 1
return # Don't know what to put here
find_chamber_discard()
print chambersinreactor # prints as 0, should be 1
print cardsdiscarded # prints as 0, should be 1
Functions shouldn't have to know what scope they're called from; the point of a function is to make a re-usable block of code that can be invoked multiple times from different places.
You communicate information to a function by passing it through its input variables. The function communicates information back to its caller by returning it.
Managing the variables of a scope is the job of the code in that scope not any functions it invokes. If you need to set variables to values determined by a function, then you have the function return those values and you use them to set the variables. If the values the function is calculating depend on the values of variables you have in the calling scope, then you need to pass them to the function as arguments. The function you're calling shouldn't have to know what variables you're using, and shouldn't be able to mess with them.
Putting that all together, what you want to do is something like this:
def find_chamber_discard(chambersinreactor, cardsdiscarded):
chambersinreactor += 1
cardsdiscarded += 1
return (chambersinreactor, cardsdiscarded)
chambersinreactor = 0;
cardsdiscarded = 0;
chambersinreactor, cardsdiscarded = find_chamber_discard(chambersinreactor, cardsdiscarded)
print chambersinreactor
print cardsdiscarded
There are ways to get around this with global variables or manipulating mutable data structures, but ultimately they make your program less flexible and more likely to contain errors that will be difficult to spot. There is a place for those techniques, but the first method you reach for to communicate information to and from functions really should be passing arguments and receiving return values.
One approach is to use mutable values, like dicts or lists:
settings = dict(
chambersinreactor = 0,
cardsdiscarded = 0
)
def find_chamber_discard():
settings['chambersinreactor'] += 1
settings['cardsdiscarded'] += 1
find_chamber_discard()
print settings['chambersinreactor']
print settings['cardsdiscarded']
However, if you have a function that is changing some state, you're probably better off wrapping that all up in class, as that's what they're for:
class CardCounter(object):
def __init__(self):
chambersinreactor = 0
cardsdiscarded = 0
def find_chamber_discard(self, hand):
for card in hand:
if card.is_chamber:
self.chambersinreactor += 1
if card.is_discarded:
self.cardsdiscarded += 1
If what you're doing is counting, maybe you could use Counter:
from collections import Counter
def test_for_chamberness(x): return x == 'C'
def test_for_discarded(x): return x == 'D'
def chamber_or_discard(card):
if test_for_chamberness(card):
return 'chambersinreactor'
if test_for_discarded(card):
return 'cardsdiscarded'
hand = ['C','C','D','X','D','E','C']
print Counter(
x for x in (chamber_or_discard(card) for card in hand) if x is not None
)
Personally, I'd go for the class approach, perhaps even wrapping Counter, as it keeps all the associated functionality together.
#!/usr/bin/env python
chambersinreactor = 0; cardsdiscarded = 0;
def find_chamber_discard():
chambersinreactor = 0
cardsdiscarded = 0
chambersinreactor += 1
cardsdiscarded += 1
return(chambersinreactor, cardsdiscarded)
#Here the globals remain unchanged by the locals.
#In python, a scope is similar to a 'namespace'
find_chamber_discard()
print chambersinreactor #prints as 0
print cardsdiscarded
#I've modified the function to return a pair (tuple) of numbers.
#Above I ignored them. Now I'm going to assign the variables in the
#main name space to the result of the function call.
print("=====with assignment===")
(chambersinreactor, cardsdiscarded) = find_chamber_discard()
print chambersinreactor # now prints as 1
print cardsdiscarded
# Here is another way that doesn't depend on returning values.
#Pass a dictionary of all the main variables into your function
#and directly access them from within the function as needed
print("=======using locals===")
def find_chamber_discard2(_locals):
_locals['chambersinreactor'] += 1
_locals['cardsdiscarded'] += 1
return
find_chamber_discard2(locals())
print chambersinreactor #incremented the value, which was already 1
print cardsdiscarded

Categories

Resources