Python programming function call scope changes - python

I am not a newbie to python. But recently I encountered an error, due to some misconception. Someone please help me to clarify it. Entire program is here : http://www.codeskulptor.org/#user39_cFs3Z8mAtf_0.py
I am having a function
def mc_trial(board, player):
"""
Plays a game starting with the given player by making random
moves and alternating between players.
"""
while board.check_win() == None:
# Get a random empty square
empty_squares = random.choice(board.get_empty_squares())
# Move the player in a random position
board.move(empty_squares[0], empty_squares[1], player)
# Switch the player
player = provided.switch_player(player)
# Game has ended
return
scores = [[0 for dummy in range(board.get_dim())] \
for dummy in range(board.get_dim())]
board_clone = board.clone()
for dummy in range(trials):
print board_clone ## Empty board
mc_trial(board_clone, player)
print board_clone #### Here value is changing after function call. How ??
My doubt is "board_clone" is passing to a function mc_trial(). the return statement there is not providing anything relating to return a value except None. But after the call, when I am printing "board_clone" the value is changing. I tried to clarify it with ipython through a sample program. But, there value remains unchanged as in the local scope . For clarification, i run a sample program, there it behaves as i expected.
def func1(var):
x = 0
while x < 1:
var[1:5]
x = x+1
return
var1 = [1,2,3,4,5,6,7,8,9]
print "B F", var1
func1(var1)
print "A F", var1

This is normal Python behaviour. Lists are passed by reference, so if you change the items in a list in a function, the changes will persist:
def f(mylist):
mylist[0] = 1
>>> l = [0, 0, 0]
>>> mylist(l)
>>> l
[1, 0, 0]
The reason your second example isn't changing the list is because the var[1:5] statement doesn't do anything. It just creates a temporary variable which is a slice of the var list, and then throws it away. It's just like saying a + 1; this would create a temporary variable but would not modify a even inside the function, let alone outside of it.

I'd guess your problem is that your "clone" has references to datastructs in the board object. The clone code doesn't appear in the link you posted?.. and as far as I can recall it's not an in-built python thing (I may be wrong there).?
Read this How to clone or copy a list?
and/or this
https://docs.python.org/2/library/copy.html

Related

How to repeat a function till its return value matches its previous return value?

I have a Python function that returns a value.
def population_gen(P)
...
return fittest
Now, I need to write a piece of code that compares the "fittest" with last iteration of the function call population_gen(P) that returned "fittest" and stops when the current fittest becomes equal to the previous one.
What I have tried is the following:
def population_gen(P)
...
return fittest
gen_count = 0
max_gen_count = 10
while gen_count <= max_gen_count:
population_gen(Pop)
gen_count += 1
I can run this any number of times (11 in this case) and make sure that my code is working. But, I do not want to it run 11 times, rather keep the while loop running till the aforementioned condition is met. I also wanted to mention that this function generates a population from a given initial population. Then, I feed this function again with the population that it generates and successfully keep it running for as many times I want. But I cannot implement the condition of comparing the "fittest" value.
Please help. Thanks in advance!
I assume that you want to keep iterating through the loop if the current and last values of the function returned is not equal.
Theory:
You can declare two variables, one inside loop and one outside. The work of inside one is to get the value of function returned by the function, and the outside one will be used to keep check of the previous value, so that it doesn't get lost (I mean the previous value here).
Now, you know, how simple it is, let's implement it. First, for the example, I would be creating a function that returns a random value from a list.
import random # Importing random module for help in choosing random value
def foo():
_list = [1, 2, 3]
return random.choice(_list)
Now, we have our example function, let's create a loop to keep check. NOTE: The type of loop you are using can be used, but the more efficient one is to use for loop, as you can use range function there as suggested by #Jab.
It's time to declare variables and the loop:
var = None # Outside the loop, will be used as a container for previous returned value
for _ in range(1, 12): # range(min. value, max. value [not included])
a = foo() # Creating a variable 'a', that would be holding current returned value from the function
if var is not None and a == var: # For first iteration, it would be None but the value will be assigned to 'var' in for it's next iteration and checking it whether the prev. and current values are equal or not
break # Break the loop
var = a # Assigns 'var' the value if the loop is still running
Now, the above example can be used as the answer. But, what if you want to check it? So, following is the code provided with the example for debugging purposes:
import random
def func():
l = [1, 2, 3]
val = random.choice(l)
print(val) # Printing the value, for debugging purposes
return val
var = None
for x in range(1, 12):
a = func()
if var is not None and a == var:
print(f'\nSame values after {x} iterations') # It will show, after how many iterations the loop stopped
break
var = a
Now, I ran the above code 3 times, the following is the output:
OUTPUT:
>>> 3
>>> 3
>>>
>>> Same values after 2 iterations
>>> 2
>>> 3
>>> 2
>>> 2
>>>
>>> Same values after 4 iterations
>>> 2
>>> 2
>>>
>>> Same values after 2 iterations
I hope you understood the concept.
Use a new default variable set as maximum in the arguments. Like -
def population_gen(P, prev_fit = 99999): if prev_fit < population_gen(P): return prev_fit
Assuming you want to call the function population_gen until it's last two invocations return the same value. The output of population_gen becomes the input in the next iteration.
import random
def population(limit: int) -> int:
return random.randint(1, limit)
prev = None
cur = population(10)
while prev != cur:
prev = cur
cur = population(prev)
print(prev)

Function in Python is treating list like a global variable. How to fix this?

I'm not sure why l would be modified by the find() function. I thought since I'm using a different variable in another function, l would not be modified by the function since it's not global.
I made sure it wasn't an error in the code by copy and pasting l = [2, 4, 6, 8, 10] before every print statement, and it returned the right outputs, meaning l is being changed by the function. I also removed the main function from the main and basically made it outright global, but it still gave the original bad results.
I'm not sure if this is an issue with my understanding of Python since I'm a beginner in it and I'm coming from Java.
Here's the code and results:
def find(list, user):
while True:
n = len(list)
half = int(n/2)
if n == 1:
if user != list[0]:
return "Bad"
else:
return "Good"
elif user == list[half]:
return "Good"
elif user > list[half]:
del list[0:half]
elif user < list[half]:
del list[half:n]
print(list)
if __name__ == "__main__":
l = [2, 4, 6, 8, 10]
print(find(l, 5)) # should print Bad
print(find(l, 10)) # should print Good
print(find(l, -1)) # should print Bad
print(find(l, 2)) # should print Good
but it returns with this
[2, 4]
[4]
Bad
Bad
Bad
Bad
You should read this question at first. why can a function modified some arguments while not others.
Let me rewrite your code for clarification.
def find(li, el):
# li is a list, el is an integer
# do something using li and el
if __name__ == "__main__":
l = [1,2,3,4]
e = 2
find(l, e)
The function find received two objects as parameters, one is li and the other is el. In main, we defined two objects, a list, we called it l, and an integer, we called it e. Then these two objects was passed to find. It should be clear that it is these two objects that passed to the function, not the name. Then your find function has access to this object, called l in main, while called li in find. So when you change li in find, l changed as well.
Hope that answers your question. And to fix this, check deepcopy.
Arguments in Python are passed by assignment. https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
In your case, that means that the list parameter of your find function is assigned the exact same list you pass in as the argument l. So, when you modify list (which is a very bad name since it shadows Python's list keyword), you also modify l, since no copy of the original was made.
You could use copy() to pass in a copy, but I think you would do well to reconsider the function as a whole, since it currently has many, many issues and you're likely to end up with a solution that won't suffer from having the original list passed in.

Python: Unexpected print values from within a simple function

I have created an simple, arbritrary function that prints out a value (1) based on what the first element from a randomly shuffled list is.
Looking at the code below: I am creating a variable and setting it to 1, then I'm a creating a small list (1,2,3) and then shuffling the list. The idea is, if the first element of that list (after shuffle) is 1 or 2, then print the value of 'correct', BUT if the first element of the list happens to be 3, set 'correct' to 0, and then run the function again. As you can see from the if statement, when 'value' is set to 0, I don't expect this to be printed at all because the function is being run again (chair()) before any printing takes place.
Having run this, I expected to find a print out of 5 values of 1. The problem is, I am seeing 0 values printed too. This seems like a very simple function, and I feel a little but stupid for not seeing why this is happenig. Any ideas?
def chair():
correct = 1
a = [1,2,3]
random.shuffle(a)
if a[0] == 3:
correct = 0
chair() #run function again without printing
print correct
for i in range(5):
chair()
Just calling chair() does not modify correct. You need to do correct = chair(). The problem with that is chair() does not return anything; it prints it. You should move the printing to outside of the function and return instead:
def chair():
correct = 1
a = [1,2,3]
random.shuffle(a)
if a[0] == 3:
correct = chair()
return correct
for i in range(5):
print chair()
Your function is recursive, so when you recall the function again to avoid printing, the function will still finish. If you added an else statement, you'd see those zeros disappear. Ex:
def chair():
correct = 1
a = [1,2,3]
random.shuffle(a)
if a[0] == 3:
correct = 0
chair() #run function again without printing
else:
print correct
for i in range(5):
chair()
edit: grammar
edit: example

variable doesn't reset in while loop with function

I have never come across this before and wonder if anyone has a solution. I have a while loop with a function inside that returns an integer. The function takes a string and a list. The first iteration of the while loop returns the correct answer but on subsequent iterations the list seems to be empty even though the function does not change the list in any way. Also if i try to reset the list after the function in the while loop the new list also seems to be empty. It seems to be very strange behavior. Any explanation as to whats going on would be much appreciated. The code of the function inside the loop is quite long so at this stage I will avoid posting it. However if requested then I will post.
spectrum = [1,2,3,4,5,6]
leaderboard = ['zzz','xxx','yyy']
pep_scores = []
j=0
original_spectrum = spectrum
print len(original_spectrum)
while j < len(leaderboard):
x= linear_score(leaderboard[j],spectrum) #this function doesn't alter spectrum
print leaderboard[j], x
spectrum = original_spectrum #should reset spectrum even though it shouldn't be necessary to do that
print len(spectrum), len(original_spectrum) #prints 2 empty lists
pep_scores.append(x) #appends correct score on 1st iteration and '0' for all others
j=j+1
I had added print statements to try to resolve the problem, my original code did not contain 'original_spectrum = spectrum' or 'spectrum = original_spectrum' in the while loop. I don't understand why after 1 iteration 'origninal_spectrum' is an empty list. I haven't posted the function because I cannot see how it could be causing the problem. Please ask if you need more information.
To create copy of the list use copy_list = original_list[:].
So in your example:
spectrum = [1,2,3,4,5,6]
leaderboard = ['zzz','xxx','yyy']
pep_scores = []
j=0
original_spectrum = spectrum[:]
print len(original_spectrum)
while j < len(leaderboard):
x= linear_score(leaderboard[j],spectrum) #this function doesn't alter spectrum
print leaderboard[j], x
spectrum = original_spectrum[:] #should reset spectrum even though it shouldn't be necessary to do that
print len(spectrum), len(original_spectrum) #prints 2 empty lists
pep_scores.append(x) #appends correct score on 1st iteration and '0' for all others
j=j+1
Its because of that you define spectrum outside the function and its scope is global , and when you pass spectrum to your function as its the name of list any changes on it change it globally not local in your function ! and note that its just about mutable (like lists) objects . (Note : labels are pointer to special memory addresses ) (your copy command original_spectrum = spectrum just make 2 label for one object !!! )
for better understanding see the below example :
>>> a=[1,2,3]
>>> def f(s):
... s.remove(1)
...
>>> f(a)
>>> a
[2, 3]
>>> def f(s):
... s+=[1,2]
...
>>> f(a)
>>> a
[2, 3, 1, 2]
Now you have 2 choice :
make a copy of spectrum and pass that to function :
copy_spectrum = spectrum[:]
define spectrum inside the function and one outside for global usage !

Function changing the value of argument in Python

I am trying to write a program for Conway's Game of Life, and I came across some really weird problems, so I'm going step by step and trying to debug it. There is some weird stuff going on.
If you are not familiar with Conway's game of Life, the rules for determining the next stage is simply:
Any live cell with fewer than two live neighbours dies, as if caused
by under-population.
Any live cell with two or three live neighbours lives on to the next
generation.
Any live cell with more than three live neighbours dies, as if by
overcrowding.
Any dead cell with exactly three live neighbours becomes a live cell,
as if by reproduction.
I'm keeping a list called squareList that has N_ROWS rows and N_COL columns. I reference each element as squareList[i][j].
My get_next(squareList) function returns another list that counts the number of 'neighbors' in each square, and returns another list with the next stage.
Now, onto my problem. Here is a test case that highlights that a function is changing values it is not supposed to:
squareList = init_list(NUM_ROWS, NUM_COL) #sets all values in squareList to zero.
#here, NUM_ROWS = 12 and NUM_COL = 18
squareList[11][17] = 1
squareList[5][7] = 1
squareList[6][7] = 1
squareList[7][7] = 1
squareList[9][2] = 1
squareList[9][3] = 1
squareList[9][4] = 1
print_list(squareList) #prints squareList
nextList = get_next(squareList) #does NOT change squareList
print '\n--------------------------------------\n'
print_list(squareList) #prints squareList again
sys.exit()
What I get when I use my print_list function is:
As you can see, everything touched by the get_next function is set to zero. This shouldn't happen in my mind for two reasons:
It's not what should happen according to the Conway logic in my get_next function (and I really cannot find why it would not work)
My get_next function is setting a nextList variable, it's not supposed to do ANYTHING to squareList !! What am I missing??
Here's the code for my get_next function:
def get_next(squareList): #gets the next list for the game of life
nextList = squareList
for i in range(1,NUM_ROWS - 1):
for j in range(1,NUM_COL-1):
#num of neighbors:
counter = sum( [squareList[i+x][j+y] for x in range(-1,2) for y in range(-1,2) if not (x==y and x == 0)])
#logic for changing:
if squareList[i][j] == 1 and counter < 2: nextList[i][j] = 0
elif squareList[i][j] == 1 and counter > 3: nextList[i][j] = 0
elif squareList[i][j] == 0 and counter == 3: nextList[i][j] = 1
return nextList
My original thought is that it's changing a global variable, but it's not the case. First, python needs to declare global variables it uses within the function, and second, I tried changing the names of the lists, and got the same results.
EDIT: reply to lanzz suggestion:
The first thing you do in your get_next function is to make nextList a reference to the same list that squareList points to. Assignment does not imply copying — nextList = squareList makes both names point to the same actual structure in memory, so any change to nextList will affect squareList as well.
You should use copy.deepcopy to obtain an actual copy of your squareList list.

Categories

Resources