Python code repeats 8 times when not necessary - python

def Reset():
global seven_digit
seven_digit = ["","","","","","",""]
global x
x = 0
global eight
eight = 0
global c
c = 0
cinput()
def cinput():
global thing
print("Enter digit ", x+1)
thing = input("")
check()
def check():
global eight
global x
if not thing.isdigit():
print("That character is not allowed")
cinput()
elif len(thing) > 1:
print("Those characters are not allowed")
cinput()
if x < 7:
seven_digit[x] = int(thing)
x += 1
cinput()
if x == 7:
eight = int(thing)
fcheck()
def fcheck(): #this section is temporary just for testing
global c
c+=1
print("This is c, ", c)
print("Test")
print(seven_digit)
print(eight)
Reset()
This is the code I have been developing as an a-level task (it is this years GCSE course) however I have stumbled across a problem where the last section in the self-created function of fcheck() repeats itself 8 times. I have used a similar process before in python and I have never seen an error like it before. I was wondering if anyone knew what I could do to fix it, thanks.

There is a mutual calling between check and cinput, so, it you call fcheck inside this chain of calls, it will be called 8 times.
If you want to call fcheck once, after the all evaluation chain, you can just remove the call to it at the last line of check, and call it at the end of Reset:
def Reset():
global seven_digit
seven_digit = ["","","","","","",""]
global x
x = 0
global eight
eight = 0
global c
c = 0
cinput()
fcheck()
def cinput():
global thing
print("Enter digit ", x+1)
thing = str(input(""))
check()
def check():
global eight
global x
if not thing.isdigit():
print("That character is not allowed")
cinput()
elif len(thing) > 1:
print("Those characters are not allowed")
cinput()
if x < 7:
seven_digit[x] = int(thing)
x += 1
cinput()
if x == 7:
eight = int(thing)
def fcheck(): #this section is temporary just for testing
global c
c+=1
print("This is c, ", c)
print("Test")
print(seven_digit)
print(eight)
Reset()

Related

Python not saving variable values when they are changed inside a function

I saved my variables at the start of my program and allow the functions to access them I believe but when they run the value is not saved when the function repeats.
P1_score = 0
P2_score = 0
round_number = 0
def dice_rolling():
# P1D1 means player ones dice one value and so on with P1D2
import random
# player ones turn
print("player ones turn")
P1D1 = random.randint(1, 6)
print("your number is ", P1D1)
P1D2 = random.randint(1, 6)
print("your second number is", P1D2)
# player twos turn
print("player twos turn")
P2D1 = random.randint(1, 6)
print("your number is", P2D1)
P2D2 = random.randint(1, 6)
print("your second number is", P2D2)
score_calculation(P1D1, P1D2, P2D1, P2D2,P1_score,P2_score,round_number)
def score_calculation(P1D1, P1D2, P2D1, P2D2,P1_score,P2_score,round_number):
import random
round_number = round_number + 1
# player 1 score calculation
total_P1 = P1D1 + P1D2
P1_score = P1_score + total_P1
if total_P1 % 2 == 0:
P1_score = P1_score + 10
else:
P1_score = P1_score + 5
if P1D1 == P1D2:
P1D3 = random.randint(1, 6)
P1_score = P1_score + P1D3
# player 2 score calculation
total_P2 = P2D1 + P2D2
P2_score = P2_score + total_P2
if total_P2 % 2 == 0:
P2_score = P2_score + 10
else:
P2_score = P2_score + 5
if P2D1 == P2D2:
P2D3 = random.randint(1, 6)
P2_score = P2_score + P2D3
print("player ones score at the end of round", round_number, "is", P1_score)
print("player twos score at the end of round",round_number,"is",P2_score)
for x in range(0,5):
dice_rolling()
Any help would be appreciated and if someone could give a simple explanation as to what I'm doing wrong and what to fix would be great.
Python can read from global variables inside a function, but can't assign them without some extra work. In general, when you want to use a global variable, it's a good idea to make it explicit by using the global keyword in your function:
my_global_var = 0
def some_function():
global my_gobal_var
my_global_var = 10
print(my_global_var) # it prints 10
somefunction() # modifies the global var
print(my_global_var) # now it prints 10
Variables are defined and used locally. Consider this example.
x = 1 #initial value
def test(x):
print(x) #print what you got
x += 1
print(x) #print updated value
print(x) #Actual printouts here
test(x)
print(x)
This results in :
1
1 #value when entering the function
2 #Value changed by function
1 #value outside the function did not change
If you want the variables to be maintained in functions, consider using class variables or global variables. (I recommend avoiding globals as you get to more complex problems)
Global example:
global x
x = 1
def test():
global x
x+=1
print(x)
test()
print(x)
test()
print(x)
Results in :
1
2
3
Finally class variables:
class test_class():
def __init__(self):
self.x = 1 #variables defined like this are class variables
def test(self):
self.x += 1 #Advantages of class variables is that you can defined them in the __init__ function
def running(self):
print(self.x) # And you can access it from multiple functions without using globals and much more too.
self.test()
print(self.x)
if __name__ == '__main__':
tclass = test_class()
tclass.running()

UnboundLocalError: local variable 'sumOfOdd' referenced before assignment in Python3

I'm trying to run this code:
number = input("Number: ")
valid = False
sumOfOdd = 0
def validation(credit_num):
for i in range(len(credit_num)):
if i % 2 != 0:
sumOfOdd += i
def main():
print(f"Your credit card number is {number}, it's third digit is {number[2]}")
print(f'sum of odds: {sumOfOdd}')
validation(number)
main()
But I get this error:
Traceback (most recent call last):
File "credit.py", line 15, in <module>
validation(number)
File "credit.py", line 8, in validation
sumOfOdd += i
UnboundLocalError: local variable 'sumOfOdd' referenced before assignment
I'm able to run, but when I input any number it gives me this error
This error occurs because the variable sumOfOdd is not accessible from within the function. You could declare the variable global in the function, but you should be careful using the global statement.
In my opinion, a better way to do this is to supply sumOfOdd as an argument to the function and return the updated variable:
def validation(credit_num, sumOfOdd):
for i in range(len(credit_num)):
if i % 2 != 0:
sumOfOdd += i
return sumOfOdd
validation(number, 0)
# Returns the correct sum.
Or, if you know that the sumOfOdd should always be initialized by zero, you could define the variable locally:
def validation(credit_num):
sumOfOdd = 0
for i in range(len(credit_num)):
if i % 2 != 0:
sumOfOdd += i
return sumOfOdd
Here's a working version of your code.
Note that it now iterates through credit_num as for num in credit_num. If you use for i in range(len(credit_num)) you are iterating through a list of indexes and would need to use if int(credit_num[i]) % 2 != 0, reason being that range(N) returns a list [0, 1, 2... N-1] where for num in credit_num iterates through [1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4] if your input string was 1111222233334444
number = input("Number: ")
valid = False
sumOfOdd = 0
def validation(credit_num):
global sumOfOdd
for num in credit_num:
if int(num) % 2 != 0:
sumOfOdd += 1
def main():
print(f"Your credit card number is {number}, it's third digit is {number[2]}")
print(f'sum of odds: {sumOfOdd}')
validation(number)
main()
Note that validation(number) is called in global scope along with main(), hence the global sumOfOdd declaration inside to allow def validation to gain access to that variable.
A different way to write this code to make it more readable would be:
if __name__ == "__main__":
number = input("Number: ")
valid = False
sumOfOdd = 0
def validation(credit_num):
sumOfOddToReturn = 0
for num in credit_num:
if int(num) % 2 != 0:
sumOfOddToReturn += 1
return sumOfOddToReturn
sumOfOdd = validation(number)
print(f"Your credit card number is `{number}`, its third digit is `{number[2]}`.")
print(f'sum of odds: {sumOfOdd}')

Python splitting code into functions [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Parameter vs Argument Python [duplicate]
(4 answers)
Closed 5 years ago.
Sorry about the length of this but I figured more info is better than not enough!!
I'm trying to split the (working) piece of Python code into functions to make it clearer / easier to use but am coming unstuck as soon as i move stuff into functions. It's basically a password generator which tries to only output a password to the user once the password qualifies as having a character from all 4 categories in it. (Lowercase, uppercase, numbers and symbols).
import random
import string
lowerasciis = string.ascii_letters[0:26]
upperasciis = string.ascii_letters[26:]
numberedstrings = str(1234567809)
symbols = "!#$%^&*()[]"
password_length = int(raw_input("Please enter a password length: "))
while True:
lowerasscii_score = 0
upperascii_score = 0
numberedstring_score = 0
symbol_score = 0
password_as_list = []
while len(password_as_list) < password_length:
char = random.choice(lowerasciis+upperasciis+numberedstrings+symbols)
password_as_list.append(char)
for x in password_as_list:
if x in lowerasciis:
lowerasscii_score +=1
elif x in upperasciis:
upperascii_score +=1
elif x in numberedstrings:
numberedstring_score +=1
elif x in symbols:
symbol_score +=1
# a check for the screen. Each cycle of the loop should display a new score:
print lowerasscii_score, upperascii_score, numberedstring_score, symbol_score
if lowerasscii_score >= 1 and upperascii_score >= 1 and numberedstring_score >= 1 and symbol_score >=1:
password = "".join(password_as_list)
print password
break
And here is my attempt at splitting it. When i try to run the below it complains of "UnboundLocalError: local variable 'upperascii_score' referenced before assignment" in the scorepassword_as_a_list() function
import random
import string
lowerasciis = string.ascii_letters[0:26]
upperasciis = string.ascii_letters[26:]
numberedstrings = str(1234567809)
symbols = "!#$%^&*()[]"
password_length = int(raw_input("Please enter a password length: "))
lowerasscii_score = 0
upperascii_score = 0
numberedstring_score = 0
symbol_score = 0
password_as_list = []
def genpassword_as_a_list():
while len(password_as_list) < password_length:
char = random.choice(lowerasciis+upperasciis+numberedstrings+symbols)
password_as_list.append(char)
def scorepassword_as_a_list():
for x in password_as_list:
if x in lowerasciis:
lowerasscii_score +=1
elif x in upperasciis:
upperascii_score +=1
elif x in numberedstrings:
numberedstring_score +=1
elif x in symbols:
symbol_score +=1
# give user feedback about password's score in 4 categories
print lowerasscii_score, upperascii_score, numberedstring_score, symbol_score
def checkscore():
if lowerasscii_score >= 1 and upperascii_score >= 1 and numberedstring_score >= 1 and symbol_score >=1:
return 1
else:
return 0
def join_and_printpassword():
password = "".join(password_as_list)
print password
while True:
genpassword_as_a_list()
scorepassword_as_a_list()
if checkscore() == 1:
join_and_printpassword()
break
The primary issue here is that you need to keep track of the scope of the various variables that you're using. In general, one of the advantages of splitting your code into functions (if done properly) is that you can reuse code without worrying about whether any initial states have been modified somewhere else. To be concrete, in your particular example, even if you got things working right (using global variables), every time you called one of your functions, you'd have to worry that e.g. lowerassci_score was not getting reset to 0.
Instead, you should accept anything that your function needs to run as parameters and output some return value, without manipulating global variables. In general, this idea is known as "avoiding side-effects." Here is your example re-written with this in mind:
import random
import string
lowerasciis = string.ascii_letters[0:26]
upperasciis = string.ascii_letters[26:]
numberedstrings = str(1234567809)
symbols = "!#$%^&*()[]"
def genpassword_as_a_list(password_length):
password_as_list = []
while len(password_as_list) < password_length:
char = random.choice(lowerasciis+upperasciis+numberedstrings+symbols)
password_as_list.append(char)
return password_as_list
def scorepassword_as_a_list(password_as_list):
lowerasscii_score = 0
upperascii_score = 0
numberedstring_score = 0
symbol_score = 0
for x in password_as_list:
if x in lowerasciis:
lowerasscii_score +=1
elif x in upperasciis:
upperascii_score +=1
elif x in numberedstrings:
numberedstring_score +=1
elif x in symbols:
symbol_score +=1
# give user feedback about password's score in 4 categories
return (
lowerasscii_score, upperascii_score, numberedstring_score,
symbol_score
)
def checkscore(
lowerasscii_score, upperascii_score, numberedstring_score,
symbol_score):
if lowerasscii_score >= 1 and upperascii_score >= 1 and numberedstring_score >= 1 and symbol_score >=1:
return 1
else:
return 0
def join_and_printpassword(password_as_list):
password = "".join(password_as_list)
print password
password_length = int(raw_input("Please enter a password length: "))
while True:
password_list = genpassword_as_a_list(password_length)
current_score = scorepassword_as_a_list(password_list)
if checkscore(*current_score) == 1:
join_and_printpassword(password_list)
break
A few notes on this:
Notice that the "score" variables are introduced inside the scorepassword_as_list function and (based on the scoping rules) are local to that function. We get them out of the function by passing them out as a return value.
I've used just a bit of magic near the end with *current_score. Here, the asterisk is used as the "splat" or "unpack" operator. I could just as easily have written checkscore(current_score[0], current_score[1], current_score[2], current_score[3]); they mean the same thing.
It would probably be useful to read up a bit more on variable scoping and namespaces in Python. Here's one guide, but there may be better ones out there.

Random number generator and validator

This is my code for a gtin-8 number generator
barcode = []
inputChoice = 0
inputChoice = 1
checkNumber = 0
placeHolder = 0
def generate():
generate = 0
for i in str(Userinput):
barcode.append(int(i))
print(barcode)
for i in range (0,6,2):
generate += barcode[i] *3
print('Generate is now ',generate)
print(generate)
def checkNumber():
for i in str(Userinput):
checkNumber += int(i)
placeHolder += int(i)
checkNumber = round(checkNumber, -1)
checkNumber = (checkNumber - placeHolder) % 10
return(checkNumber)
def check():
jk = 0
def main():
inputChoice=0
while inputChoice !=9 and inputChoice !=1 and inputChoice!=2:
inputChoice=int(input("chose 1 to get GTIN,2 to check,9 to exit\n"))
Userinput = (input("input the barcode \n"))
if inputChoice==1 :
if len(Userinput) !=7:
print("make sure you inputted 7 inegers")
if inputChoice == 2:
if len(Userinput) !=8:
print("Make sure you input 8 integers")
else:
generate(Userinput)
return(Userinput)
Userinput = main()
generate()
checkNumber()
I have made a function called check number, generate and main but when I run a code it gives me this error:
TypeError: generate() takes 0 positional arguments but 1 was given
I am unsure why this is the case, jk = 0 is there as I have not finished that function and I am just using it as a placeholder.
works fine after adding *args, than we get
UnboundLocalError: local variable 'checkNumber' referenced before assignment
so add default args in the flag of checkNumber()
def generate(*args): #<<<< edit
generate = 0
for i in str(Userinput):
barcode.append(int(i))
print(barcode)
for i in range (0,6,2):
generate += barcode[i] *3
print('Generate is now ',generate)
print(generate)
def checkNumber(checkNumber=0, placeHolder=0): #<<<< edit
for i in str(Userinput):
checkNumber += int(i)
placeHolder += int(i)
checkNumber = round(checkNumber, -1)
checkNumber = (checkNumber - placeHolder) % 10
return(checkNumber)
that its works finaly result with 1515155155151 as barcode:
chose 1 to get GTIN,2 to check,9 to exit
2
input the barcode
1515155155151
Make sure you input 8 integers
[1, 5, 1, 5, 1, 5, 5, 1, 5, 5, 1, 5, 1]
Generate is now 3
Generate is now 6
Generate is now 9
9
Ok you asked for feedback so here we go...
Point #1: dont use globals when you dont need them. Hint : you can write huge (10+klocs) programs without a single global (talking about globals that are modified or reassigned during execution of course - pseudo-constants etc are ok). Practicaly, this starts by writing pure functions - functions that take arguments, return results, and for which the same set of arguments will always produce the same results
Point #2: respect the language's usual coding conventions (cf pep8 for Python)
Point #3: learn the language's idioms - in your example,
use of str.format()
use of list comprehensions or generator expressions instead of for loops where it makes sense,
a main() function that is really a main function and is guarded by a if __name__ == "__main__": check)
Here's an example of what your code could look like once those points are fixed:
def generate(source):
barcode = [int(c) for c in source]
result = sum(barcode[i] * 3 for i in range(0, 6, 2))
return result, barcode
def check_number(source):
placeholder = checksum = sum(int(i) for i in source)
checksum = round(checksum, -1)
checksum = (checksum - placeholder) % 10
return checksum
def check(what):
jk = 0
def main():
choice = 0
while True:
choice = input("chose 1 to get GTIN, 2 to check, 9 to exit\n").strip()
try:
choice = int(choice)
if choice not in (1, 2, 9):
raise ValueError
except ValueError:
print("Invalid value '{}'".format(choice))
continue
if choice == 9:
return
while True:
source = input("input the barcode \n").strip()
# source should contain only digits
try:
int(source)
except ValueError:
print("Make sure you entered only integers")
continue
if choice == 1 and len(source) != 7:
print("Make sure you entered 7 integers")
continue
elif choice == 2 and len(source) != 8:
print("Make sure you entered 8 integers")
continue
else:
# ok, valid input
break
if choice == 1:
result, barcode = generate(source)
print("barcode : {}.".format(barcode))
print("result : {}".format(result))
elif choice == 2:
raise NotImplementedError("check is not yet implemented")
if __name__ == "__main__":
main()

Do while loops have local variables in Python?

I am trying to use a while statement like so:
o = 0
while o == 0:
try:
n = int(raw_input("Which number do you want to begin with?"))
o = 1
except:
o = 0
print "Please use a valid number."
However, when I try to use variable n later, it gives me the "local variable 'n' referenced before assignment' UnboundLocalError. That means that n cannot be recognized as a variable in the def I am using, because it only exists in the while statement? Is this possible?
The whole code:
import time
from sys import argv
import os
os.system("cls")
print "Welcome to Number counter 2.0!"
a = True
def program():
global a
if a == False:
os.system("cls")
o = 0
while o == 0:
try:
n = int(raw_input("Which number do you want to begin with?"))
o = 1
except:
o = 0
print "Please use a valid number."
if n == "/historyKeep false":
if a == False:
print "Command historyKeep is already set to false."
else:
a = False
print "Command set successfully."
elif n == "/historyKeep true":
if a == True:
print "Command historyKeep is already set to true."
else:
a = True
print "Command set successfully."
if n == "/historyKeep false":
n = raw_input("Which number do you want to begin with?")
elif n == "/historyKeep true":
n = raw_input("Which number do you want to begin with?")
d = raw_input("How many seconds between each number?")
d = int(d)
total_s = n * d
while n > 0:
print n
time.sleep(d)
n = n - 1
print "Done in", total_s, "seconds in total!"
end_q = raw_input("Exit or retry? (e/r)")
if end_q == "e":
os.system("cls")
print "Exiting."
time.sleep(0.5)
os.system("cls")
print "Exiting.."
time.sleep(0.5)
os.system("cls")
print "Exiting..."
time.sleep(0.5)
os.system("cls")
exit(0)
elif end_q == "r":
program()
program()
You set a = True at the beginning. You then test if a == False and only set n if it is. But then you test n == "/history.... n has not been set at this point.
You need to make sure n is assigned before you use it. It is not enough to just mention it in a branch that is not taken.
n is not defined in the scope that you are trying to use it to fix this define it outside of the while loop and the if statement the while loop is in:
global a
n = 0
Then when you ask the user for what number to start with, that value will replace 0, and you should be good to go. Also instead of declaring global a, why not just make a an input argument for the program() function?
Just to make sure, declare n outside of the loop first:
n = None
while True:
try:
n = int(raw_input("Text..."))
break
except:
print("Please enter a valid number!")
Note: Usually, you would use break to exit a loop. This is because your method requires an extra variable, which uses more memory (not much, but if you keep doing it, it will stack up).

Categories

Resources