Python random module doesn't randomize more than once - python

in this code there are two Random Number Generators. One in the first line, the other in the function btn_yes.
Both RNG's work fine, the print(nb) directly after the generator in the function btn_yes displays a random number like it should.
However, when btn_press is activated after btn_yes was activated (like it should in the program), the value of nb doesn't change, no matter how often i execute btn_yes. btn_press just uses the same number that was generated by the first RNG.
What am I missing?
nb = random.randrange(0, 11)
def btn_press():
guess = int(entry.get())
print(guess)
if guess < nb:
answ["text"] = "Higher!"
elif guess > nb:
answ["text"] = "Lower!"
elif guess == nb:
answ["text"] = "Correct!"
btn2["bg"] = "#FF6C6C"
btn3["bg"] = "#32FF00"
def btn_no():
window.destroy()
def btn_yes():
answ["text"] = "Next Round! Type in a new number!"
btn2["bg"] = "#464646"
btn3["bg"] = "#464646"
nb = random.randrange(0, 11)
entry.delete(0, tk.END)
print(nb)

The problem here is that when you change a global variable inside a function, you need to put global <variablename> as the first line in the function. Otherwise, Python assumes you meant to make a new variable with that name that is only in scope inside the function, and the global variable remains unchanged.
You can see this by printing nb inside the function btn_yes(); you should see that it has a different value each time (and not the same value as the global nb).
In this case, if you put global nb as the first line in btn_yes(), it should have the desired effect.
See using global variables in a function

Related

Conceptual understanding of variable scope in higher order functions

I have a function score0 which gives me a score for every turn (for n turns) in a loop. This score is incremented by a random integer from 1 to 15 every turn.
I now have to design another higher order function which should print the highest score jump of a player out of all the score jumps yet, and should be called inside the score0 function. I name it highest_gain. Naturally, this should print the first score value as it's the first turn (hence it is the biggest jump).
# Function that defines the highest point jump in a score yet
import random
def highest_gain(previous_value, highest_point):
def say(score) :
if previous_value == 0:
print ('Biggest gain by player0 yet with',score,'points!')
return highest_gain(score, score)
gain = score - previous_value
if gain > highest_point:
print('Biggest gain by player0 yet with',score,'points!')
return highest_gain(score, gain)
return say
# Function that gives me a new score (incremented) every turn
def score0(n, score = 0):
while n > 0:
score += random.randint(1, 15)
highest_gain(previous_value = 0,highest_point = 0)(score)
n -= 1
return score
#Calling the function
score0(4,0)
Python Tutor link
The problem is that calling highest_gain() doesn't update the values of previous_value and highest_point. Why aren't these variables getting updated in the score0() function body, and how should highest_gain() be called be called so that these variables are updated on each iteration of the loop?
Your highest_gain function is a higher-order function which returns another function named say. When say is called, it calls highest_gain again and returns the result, which is - again - the function say. The important point here is that say is a closure over the local variables of the outer function highest_gain, so each time highest_gain is called you get a different instance of the say function, with different values of the outer function's local variables.
Now, since calling say returns another instance of say which closes over the updated values, that means you need to keep the result from when you call it, so you can call the new instance which closes over those updated values.
def score0(n, score=0):
say = highest_gain(previous_value=0, highest_point=0)
while n > 0:
score += random.randint(1, 15)
say = say(score) or say
n -= 1
return score
I moved the original call of highest_gain to before the loop, since you don't want to use the initial values of 0 on every iteration.
Note that say doesn't always return a new instance of say - sometimes it returns None, so I used the trick say(score) or say here so that say keeps its old value if there isn't a new one to update it with. You could alternatively write this as below, which is more verbose but perhaps makes clearer what this is doing:
new_say = say(score)
if new_say is not None:
say = new_say
Otherwise, you could change the definition of say so that it returns itself (i.e. with the current values of the variables from the outer function) when there should be no update, and then in score0 you could just write say = say(score).

Function vs if-statement: Function is not working, but the code in the function will work when outside a function?

I was working on building a randomized character generator for Pathfinder 3.5 and got stuck.
I am using the Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill): function to populate a randiomized list of skills with their class based points total, class bonus, and point buy. So modelling the action of a player picking skills for their character.
As an example below, Wizards.
I pick Knowledge_Arcana as a skill and spend one of my skill point pool (Calculated by taking my intelligence modifier +2) on it. So that skill now equals my intelligence modifier(+1 in this case), class skill bonus as a wizard (+3), plus the point I spent(+1) for a total of 5.
The problem is while the function prints the correct result of 5, the outstanding variables do not populate with the final total. To continue our example I'd run the function on Knowledge_Arcana, get a +5, and then check the Knowledge_Arcana after the function call and get just +1. Conversely, if I write out the function as just an if statement it works. Example is next to the function for comparison.
Does anyone know why Im getting the different result?
## Creating the lists and breaking into two separate sections
Int_Mod = 1
Skill_Ranks = 3
Rand_Class = 'Wizard'
Knowledge_Arcana = Int_Mod
Knowledge_Dungeoneering = Int_Mod
Wizard_Class_Top_Skills = ["Knowledge_Arcana"]
Wizard_Class_Less_Skills = ["Knowledge_Dungeoneering"]
Class_Skill = 3
Important_Skills_Weighted = .6
Less_Important_Skills_Weighted = .4
Important_Skills_Total_Weighted = round(Skill_Ranks*Important_Skills_Weighted)
Less_Skill_Total_Weighted = round(Skill_Ranks*Less_Important_Skills_Weighted)
Wiz_Draw =['Knowledge_Arcana', 'Knowledge_Dungeoneering']
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String)
print(Skill_String, Skill_List)
else:
print('Nuts!')
## Function Calls
Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill)
Populate_Skills('Knowledge_Dungeoneering', Wiz_Draw, Knowledge_Dungeoneering, Class_Skill)
print(Knowledge_Arcana,Knowledge_Dungeoneering)
Edited to be a MRE, I believe. Sorry folks, Im new.
You are passing in a reference to a list and expect the function to modify it; but you are reassigning the variable inside the function which creates a local variable, which is then lost when the function is exited. You want to manipulate the same variable which the caller passed in, instead.
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List.extend(Class_Skill + Draw.count(Skill_String))
print(Skill_String, Skill_List)
else:
print('Nuts!')
Alternatively, have the function return the new value, and mandate for the caller to pick it up and assign it to the variable.
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String)
print(Skill_String, Skill_List)
else:
print('Nuts!')
return Skill_List
Skill_List = Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill)
# etc
You should probably also rename your variables (capital letters should be used for classes and globals; regular Python functions and variables should use snake_case) and avoid using global variables at all. The entire program looks like you should probably look into refactoring it into objects, but that's far beyond the scope of what you are asking.

module does not see my values inside the main code

I'm very new to Python and currently am building a GUI interface in Tkinter and have a fully working code for it. I thought it would be nice to have a button work from keyboard by pressing "Enter" key.
The code line for that is:win.bind('<Return>', testButton.functionName)
Then you just put "event" in function bracket aka functionName(event) And everything works great.
The issue is that now it does not work when you press the button inside the GUI interface what means I need to duplicate and reName one of the functions to have both.
Since it is an identical code, it would make more sense to have it as a module. However, when I import my module it does not seem to see the rest of my code and claims that my variables do not exist.
Here is a simplified example of how I'm doing it.
My module saved as moduleTest:
def test():
sum = a + b
return sum
Main program:
import moduleTest
a = 10
b = 12
moduleTest.test()
print test()
It keeps claiming that a and b are not defined.
I'm clearly misunderstanding how modules work cause in my mind when I import a module it should simply import the whole code and do the same thing as this:
import moduleTest
a = 10
b = 12
def test():
sum = a + b
return sum
print test()
What am I misunderstanding and doing wrong?
Python globals are global to each module, not to the whole program.
The usual way to deal with this is to not use globals.
For your toy example, you can def test(a, b): and then call test(a, b) from your main module, and that's typically the right solution, but it isn't always appropriate.
Other options are:
Use a class, and pass the values in when you create the instance.
Store the globals that need to be shared in a third module, which both main and moduleTest can import thirdmodule and access as thirdmodule.a. This is often appropriate for cases like shared configuration values.
Store the globals as attributes of some other thing both pieces of code already have access to.
As #abarnert points out, globals are global to each module. In order to solve this problem you can use:
def test(a, b):
sum = a + b
return sum
And
import moduleTest
a = 10
b = 12
print(moduleTest.test(a, b))
I would like to point out you should not call your variables sum because this already is an existing function which you're overwriting.
Here are several separate fragments of my code to give you an idea:
item = "SNAKE"
oldString = "-" * (len(item))
blank = "-" * (len(item))
guesses = 10
def gameOn():
global guesses
global oldString
newString = ""
i = 0
x = len(item)
hanged = ""
readString = answerIn.get().upper()
winner = 1
if not readString:
if guesses == 10:
pass
else:
hanged = (HANGMAN[(len(HANGMAN) -1) - guesses])
hangedOutput.config(state=NORMAL)
hangedOutput.delete("1.0",END)
hangedOutput.insert(END,hanged)
hangedOutput.config(state=DISABLED)
error.config(state=NORMAL)
error.delete(0,END)
error.insert(0,"Please enter the value.")
error.config(state=DISABLED)
win = Tk()
answerIn = Entry(win,width = 3, border = 2)
answerIn.focus_set()
answerIn.grid(row=2, column=0, pady = 10, padx = 200, sticky = W)
win.mainloop()
When I turn it into a module it keeps asking not just for item and oldString definitions but also for answerIn, hangedOutput and etc which are part of Tkinter labels.

Python quiz difficulty function

I'm new to Python and am really struggling to create a function for my quiz: I have created a while loop to offer different levels to the quiz player before they receive any questions. The while loop works on its own, but when i try to turn it into a function, the level variable stops working (returning only empty brackets outside of the code).
This is the while loop:
request_count = 0
level = ()
global_string_list = ()
while request_count < 1:
print user_level_options
level_request = raw_input("> ")
if level_request == "1":
level = string_1
global_string_list = string_1_list
if level_request == "2":
level = string_2
global_string_list = string_2_list
if level_request == "3":
level = string_3
global_string_list = string_3_list
if level_request == False:
print "Please try again."
request_count = request_count + 1
Could anybody please give me pointers on how to turn this into a function please?
Thank you!!
The level variable returns empty brackets because the level within the function is a local variable; completely different from the level you declared outside the function, which is outside the function's scope. This outside variable was hidden behind the local level variable, so all your assignments within the function were made to the local level and did not affect the level on the outside at all.
You have to either declare level as a global variable explicitly (generally considered bad practice) or you return your local level from the function. So perform all the changes you want in your function and then use the return statement like this:
def user_select(): # your function
#......your code goes here
return level
level = user_select() # the level variable within your function is now in this variable
I try to give you a jump start. But I won't give you the final solution.
In general, to make a function meaningful, you have to know why you need it.
I give you some example of the benefit using function.
Removal of repetitive code. (DRY, don't repeat yourself)
Identify a Pattern, and extract into a separate function. In your example, the repeating behavior can be seen near the condition inside the loop. (right after the raw_input function).
That is: if level_request == <something> then do <some-work>.
Thus, one improvement could be:
def updateLevel(level_request):
# Think about how you can determine the level without if-else
level = <?level?>
global_string_list = <?global_level|level?>
such that you only need to call:
while request_count < 1:
print user_level_options
level_request = raw_input("> ")
updateLevel(level_request)
if level_request == False:
print "Please try again."
request_count = request_count + 1
Abstraction
This is a very important topic in Computer Science and General Programming. You have to understand more to be able to appreciate the importance of abstraction.
E.g.
def askLevel():
print(user_level_options)
return raw_input("> ")
def displayError():
print("Please try again")
def incrementRequestCounter():
request_count += 1
such that you just need to call:
while request_count < 1:
level_request = askLevel()
updateLevel(level_request)
if level_request == False:
displayError()
incrementRequestCounter()
Note: in strict functional programming, function is a mapping of (input)->(output). And the function does not modify any global state (immutability). And all function must return a value. But in practice, we normally use a procedure instead. (procedure is a sequence of instruction). And def in python is both procedure and function.

Python: variable is undefined when calling function containing the variable

I just started my first programming class a few weeks ago and I'm embarrassed to say I'm very stuck. We had to create a program that (in my Professor's words):
Simulate the roll of two dice. Use a randomly generated integer to represent the roll of each die in a function named point. Return the combined value of a roll. Use a loop in main to roll the dice five times and report each result.
So, I did my best and keep getting the same issue where it is telling me my variable total is not defined, even though I'm calling the function which contains the variable.
I submitted the below code to my professor, who in turn responded:
The dice program is close. Return the total of a roll. Call point in main and capture the returned value for printing.
So he is saying to call the function point in my main function (which, at least I think, I am) but it still won't read my vital variable to finishing this.
import random
min=1
max=6
def main():
for roll in range(5):
point()
print(total)
def point():
roll=random.randint(min, max)
roll2=random.randint(min, max)
total=roll+roll2
return total
main()
Inside the main function, this line:
point()
does not make total available in the current scope. Instead, it simply calls the function point and then discards its return value.
You need to capture this return value by assigning it to a variable named total:
def main():
for roll in range(5):
################
total = point()
################
print(total)
Now, when you do print(total), total will be defined and equal to the value of point().
You're trying to reference a variable that only exists within the scope of the point() function. But you don't need to since you return the value anyway.
Try this.
from random import randint
def rollD6():
return randint(1,6)
def point():
return rollD6()+rollD6()
def main():
for roll in range(5):
print point()
main()

Categories

Resources