Why is a string printed although print isn't used? - python

This is an example program from the Python book that I am using to learn. What does the message += line signify? And how does it print that statement even though we've never used a print there?
This works as expected and returns $number is too high or $number is too low. But how does it do so even without print statement?
And what does message += str(guess) + .... do here, given that we've declared message as an empty string in the beginning (message = "")?
import random # We cover random numbers in the
rng = random.Random()
number = rng.randrange(1, 1000)
guesses = 0
message = ""
while True:
guess = int(input(message + "\nGuess my number between 1 and 1000:"))
guesses += 1
if guess > number:
message += str(guess) + " is too high.\n"
elif guess < number:
message += str(guess) + " is too low.\n"
else:
break
input("\n\nGreat, you got it in "+str(guesses)+" guesses!\n\n")
I tried using message like in the above program in one of my other scripts and it doesn't print statement like it does in the above program.

The call to input() includes message in its prompt.
Addition of strings concatenates them in Python. "ab" + "cd" produces "abcd". Similarly, message += "stuff" adds "stuff" to the end of the variable's earlier value. If it was the empty string before, "" + "stuff" produces simply "stuff".
The operator += is an increment assignment; a += b is short for a = a + b. (Some people also dislike the longer expression because it is mathematically implausible unless at least one of the values is zero; but = does not have its mathematical semantics anywhere else in Python, either.)

Related

Why does my code keep on looping like this when I'm using functions?

So I'm trying to make a Hangman console game and im trying to check how many tries I have left and then later add a game over function if tries left. One part of it is working the stringcount function is working properly until I have to call the goAgain function, I'll explain the issue lower in the document
import time
hangmanSolved = "What 'eva hh"
chosingletter = input("Input a letter you would like to try! ")
def goAgain(defaultTries):
defaultTries -= 1
print("Head drawn!", defaultTries, "tries left ")
stringcount()
# if a letter is in, and how many letters are in
def stringcount():
count = 0
lettersIn = hangmanSolved.count(chosingletter)
for i in hangmanSolved:
if i == chosingletter:
count = count + 1
if not count:
print("There are no ", chosingletter, "'s in this sentence! ")
time.sleep(1)
goAgain(defaultTries=5)
elif count == 1:
print("There is only one ", chosingletter, " in this sentence! ")
else:
print("There is ", lettersIn, chosingletter, "'s in this sentence! ")
stringcount()
When I run the code and guess a wrong letter, or rather when I have to call goAgain function it keeps on looping me this output:
Input a letter you would like to try! j
There are no j 's in this sentence!
Head drawn! 4 tries left
There are no j 's in this sentence!
Head drawn! 4 tries left
How can I fix this looping, and the tries counter?!?
So first of all the counter is wrong because you are using a local variable. When the method is called with defaultTries = 5 it will always start at 5 even though you have done defaultTries -= 1.
To fix this you need to read up and assign a global variable outside of the scope so that whenever the user is wrong that global variable gets subtracted.
Furthermore, I am unsure as to why you would use the time module here. What is your goal by doing this?
What you are trying to do is loop through the sentence and check if the letter the user inputs exists inside the sentence. There are easier ways to go about this.
You can use string.indexOf('a'). where 'a' you can change it to the letter you are searching for (the user's input). It returns the index of the first occurrence of the character in the character sequence represented by this object, or -1 if the character does not occur.
I would start with changing those things first!
Every time you call goAgain you call it with defaultTries set to 5. Then, in that function, it subtracts one, leaving you with 4. Then it calls stringount which again calls goAgain which will call stringcount and so on. There's nothing that breaks out of the loop.
So you need to change three things:
Don't pass in the same value of defaultTries every time you call goAgain. You want to allow that variable to decrease.
Git rid of the goAgain function. Instead put that logic in stringcount itself.
Check the value of the number of tries remaining and if it's zero, print out some kind of message, but don't call stringcount in that branch.
def stringcount(tries = 5):
count = 0
tries =- 1
if tries == 0:
print("No tries remaining")
else:
lettersIn = hangmanSolved.count(chosingletter)
for i in hangmanSolved:
if i == chosingletter:
count = count + 1
if not count:
print("There are no ", chosingletter, "'s in this sentence! ")
time.sleep(1)
stringcount(tries)
elif count == 1:
print("There is only one ", chosingletter, " in this sentence! ")
else:
print("There is ", lettersIn, chosingletter, "'s in this sentence! ")
stringcount()
A few other things you can consider changing:
The standard formatting in Python is PEP8 and you should review those. For example, Python convention is to use snake_case instead of camelCase. So you would use something like default_tries instead of defaultTries for example.
It's probably more explicit to check if count == 0 than it is to check if not count.
I think you don't need the loop that updates the variable count. Just use lettersIn (which you should rename as letters_in or, better, just call this count). Now you also don't need to set count==0 at the top of the function.
You're having problems because you are basically using a recursive strategy but you haven't defined a base case, and also because you reinitialize your defaultTries variable to 5 every time you call the goAgain() function. A base case is a condition where you tell a recursive function to stop. In this case tries needs to be 0. Here's an example of what your code might look like:
def goAgain(tries):
tries -= 1
if tries==0:
print("You lose")
return None
print("Head drawn!", tries, "tries left ")
stringcount(tries)
def stringcount(defaultTries=5): #This is how you set default args
chosingletter = input("Input a letter you would like to try! ")
count = 0
lettersIn = hangmanSolved.count(chosingletter)
for i in hangmanSolved:
if i == chosingletter:
count = count + 1
if not count:
print("There are no ", chosingletter, "'s in this sentence! ")
time.sleep(1)
goAgain(defaultTries)
It's up to you whether you want to keep goAgain() and stringcount() separate. I think it makes sense to keep them separate because I can imagine you want to check how many tries are in goAgain and then print new statements accordingly, like "left arm drawn!" etc.

My while loop never reaches the conditional stament and keeps looping

The assignment was to make a guessing game where the parameter is the answer. At the end if the person gets it right, it prints a congratulatory statement and returns the number of tries it took, if they type in quit, it displays the answer and tries == -1. other than that, it keeps looping until they get the answer correct.
def guessNumber(num):
tries = 1
while tries > 0:
guess = input("What is your guess? ")
if guess == num:
print ("Correct! It took you" + str(tries)+ "tries. ")
return tries
elif guess == "quit":
tries == -1
print ("The correct answer was " + str(num) + ".")
return tries
else:
tries += 1
When i run it, no matter what i put in it just keeps asking me for my guess.
Since you called your variable num so I'm guessing it's a integer, you were checking equality between an integer and a string so it's never True. Try changing the num to str(num) when comparing, so:
def guessNumber(num):
tries = 1
while tries > 0:
guess = input("What is your guess? ")
if guess == str(num):
print ("Correct! It took you {0} tries. ".format(tries))
return tries
elif guess == "quit":
tries = -1
print ("The correct answer was {0}.".format(num))
return tries
else:
tries += 1
Is the code properly indented?
The body of the function you are defining is determined by the level of indentation.
In the example you pastes, as the line right after the def has less indentation, the body of the function is 'empty'.
Indenting code is important in python
Additionally, for assigning one value to a variable you have to use a single '=', so the:
tries == -1
should be
tries = -1
if you want to assign the -1 value to that variable.

can't see a way around global var

I am just learning to script python working my way thru MIT comp. science open course and I have a question. I have read that setting vars to global inside of functions is the root of, well at least some evil. Although I am sure there is an obvious answer to my question, I could not see a way around NOT setting a global var in my func 'rand_guess' because I need the values from the func in the main body's while loop to perform further processing. I hope then someone might describe a more acceptable (pythonic I think is the term) way I could do so. Here is my script:
# Instruct the user to pick an arbitrary number from 1 to 100 and proceed to
# guess it correctly within 10 attempts. After each guess, the user must tell
# whether their number is higher than, lower than, or equal to your guess.
def rand_guess(l, h):
# print "l: " + str(l)
# print "h: " + str(h)
# global rand_num
rand_num = random.randint(l, h)
print(str(cntr) + '_2: ' + str(rand_num)) # debug
if rand_num != user_num:
if cntr < attempts:
print '\nMy guess of ' + str(rand_num) + ' is wrong.'
# global hi_lo
hi_lo = raw_input('So should my next guess be [H]igher or [L]ower? ')
print(str(cntr) + '_2: ' + hi_lo) # debug
else:
print '\nMy guess of ' + str(rand_num) + \
' is wrong, and I am out of guesses.\n'
exit()
else:
print '\nWoW! I can hardly believe it! My guess is correct: ' + \
str(user_num) + '\n'
exit()
return rand_num
return hi_lo
import random
attempts = 5
rand_num = 1
hi_lo = ''
lo = 1
hi = 100
user_num = int(raw_input('\nEnter a number between 1 to 100: '))
cntr = 1
while cntr < (attempts+1):
# first guess will be a random num between 1 to 100 so first lo_num must be
print(str(cntr) + '_1: ' + hi_lo) # debug
print(str(cntr) + '_1: ' + str(rand_num)) # debug
# 1
if cntr == 1:
rand_guess(lo, hi)
else:
# if user indicated to guess Higher with next guess
print(str(cntr) + '_3: ' + hi_lo) # debug
if hi_lo == 'H':
# then last number guessed +1 should be the lowest number guessed
# for all future guesses
print(str(cntr) + '_3: ' + str(rand_num)) # debug
if rand_num > lo:
lo = rand_num + 1
# user indicated to guess Lower with next guess
else:
# then last number guessed -1 should be the highest number guessed
# for all future guesses
if rand_num < hi:
hi = rand_num - 1
# func call with new lo-hi guess brackets
rand_guess(lo, hi)
cntr += 1
In addition, I would appreciate reading any others constructive suggestions regarding obvious python formatting faux pas I might be making, as I realize format in python is of the utmost importance.
EDIT:
Ok so I replaced the global statements with return statements, like what I originally tried and still the values I expect to be returned seem to be causing me problems. I added in some additional print statements to try to help determine what value a given var is at different points in the code. Here is the output of this script, including python debug details:
Enter a number between 1 to 100: 50
1_1:
1_1: 1
1_2: 61
My guess of 61 is wrong.
So should my next guess be [H]igher or [L]ower? L
1_2: L
2_1:
2_1: 1
2_3:
Traceback (most recent call last):
File "ex_loops2.py", line 67, in <module>
rand_guess(lo, hi)
File "ex_loops2.py", line 10, in rand_guess
rand_num = random.randint(l, h)
File "/usr/lib/python2.7/random.py", line 241, in randint
return self.randrange(a, b+1)
File "/usr/lib/python2.7/random.py", line 217, in randrange
raise ValueError, "empty range for randrange() (%d,%d, %d)" % (istart, istop, width)
ValueError: empty range for randrange() (1,1, 0)
02:53 exercises $
The first empty value for print '2_1: ' should have been 'H' (in this instance) and the value '1' for the second `print' '2_1: 1' right before pythons throws its error I expected to be '2_1: 61' (again for this particular instance, the returned value of 'rand_num').
So again, if I simply global those vars the script works as expected. I am sure someone can point out my obvious oversight or omission or wrong assumption and use of python's return statement here, because I am stumped.
While I can see how Justin's modified example below is a much better revision of what I am trying to accomplish here, I really just to need to know what it is I am doing wrong trying to return these particular var values so they are available outside of the func in which they are assigned (since the script basically works otherwise). I think remaining in the context I am having problems will yield the greatest benefit regarding this basic aspect of python.
This isn't exactly what you are looking for, but it should give some pointers.
import sys
import random
def random_guess(low=1, high=100, attempts=10, hints=True):
"""Try to guess the random number.
Args:
low (int): Lowest number the value can be.
high (int): Highest number the value can be.
attempts (int): How many tries you get.
hints (bool): Change range boundaries.
"""
# Command line args come in as strings.
low = int(low)
high = int(high)
attempts = int(attempts)
hints = hints in [True, 1, "True", 'true', '1', 't', 'y', 'yes']
mynum = None
rand_num = random.randint(low, high)
for _ in range(attempts): # _ means the last command ... i isn't used. Skips the pylint warning
# Check input
if hints and mynum is not None:
if mynum < rand_num:
low = mynum + 1
print("Incorrect answer! Guess higher next time")
elif mynum > rand_num:
high = mynum - 1
print("Incorrect answer! Guess lower next time")
elif mynum is not None:
print("Incorrect answer!", end=" ")
# Ask the user for input (raw_input for python 2.x)
try:
mynum = int(input('Enter a number between '+str(low)+' to '+str(high)+': '))
if mynum == rand_num:
print("Congratulations, you win!")
return # skip the print you lose and exit the function
except ValueError:
print("That wasn't a number.")
# end for
print("Sorry you lose!")
# end main
# Use name to check if it is the main. This prevents the below code from executing on import.
# So another application and import the random_guess method and use it without actually running it.
if __name__ == "__main__":
commandline_args = sys.argv[1:] # First argument is usually the filename
random_guess(*commandline_args) # run the main method

PYTHON: Error Message in random.py? Used to work, then it didn't, then it did, now it doesn't again. What's happening?

I've been learning python for a few months now, and usually I've been able to overcome all the problems I face, but now I'm at a loss. I'm writing a program called 'Quizzer' that will be used to generate random questions based on lists of terms and answers that Python is given.
My main problem has with the the gen_question function I've been working on. I wanted Python to receive a term, and output four multiple choice answers: One the actual, and three randomly selected from the pool of all possible answers. I had to include several checks to make sure the selected random answers were not the real answer and were not the same as each other.
I finally got it to work today, and then a bit later I tested it. I got an error message (that I will display in a second). I undid everything back to where I was earlier and I still got the same error message. After a few hours I came back, and I got it again. Out of frustration, I retried, and it worked. Now it isn't working anymore. Please, anyone: What is going on?
Here is my code (I don't know what's necessary so I am including the entire thing):
#import random for generating
import random
#term and definition libraries
terms_ans ={'term1':'answer1','term2':'answer2','term3':'answer3','term4':'answer4','term5':'answer5','term6':'answer6','term7':'answer7','term8':'answer8','term9':'answer9','term10':'answer10','term11':'answer11','term12':'answer12','term13':'answer13','term14':'answer14','term15':'answer15','term16':'answer16','term17':'answer17','term18':'answer18','term19':'answer19','term20':'answer20'}
term_list = ['term1','term2','term3','term4','term5','term6','term7','term8','term9','term10','term11','term12','term13','term14','term15','term16','term17','term18','term19','term20']
answer_list = ['answer1','answer2','answer3','answer4','answer5','answer6','answer7','answer8','answer9','answer10','answer11','answer12','answer13','answer14','answer15','answer16','answer17','answer18','answer19','answer20']
#picks the test questions to ask
def gen_test(amount=len(term_list)):
found_starter = False
test_terms = []
while found_starter == False:
#pick a random starting point in the terms to see if it is suitable
start_point = random.randint(1, len(term_list))
if amount == len(term_list):
#if user inputs max amount of questions possible, just take the term list
test_terms = term_list
found_starter = True
elif len(term_list) - (start_point + amount) >= 0:
#if it is suitable, then append the terms to the test questions
for x in xrange(start_point,start_point+amount):
test_terms.append(term_list[x])
found_starter = True
else:
return test_terms
#scramble list
def list_scrambler(unscrambled_list):
test_terms=[]
countdown = len(unscrambled_list) + 1
for x in range(1, countdown):
transfer_var = random.randint(0,len(unscrambled_list)-1)
test_terms.append(unscrambled_list[transfer_var])
del unscrambled_list[transfer_var]
return test_terms
#ask user for amount of questions needed and get the list
test_terms = list_scrambler(gen_test(int(raw_input("How many questions on your test? (There are " + str(len(term_list)) + " questions in total.) "))))
def gen_question(picked_term, question_num=1, total_amount=len(test_terms)):
#print start of question
print
print "Question " + str(question_num) + " of " + str(total_amount) + ":"
print
print picked_term
print
#gather random multiple choice answers they must a) all be different and b) not be the answer
ans_1_acceptable = False
while ans_1_acceptable == False:
int_rand_ans_1 = random.randint(1, len(term_list)) - 1
if str(term_list[int_rand_ans_1]) != str(picked_term):
#Term accepted; send to output
ans_1_acceptable = True
ans_2_acceptable = False
while ans_2_acceptable == False:
int_rand_ans_2 = random.randint(1, len(term_list)) - 1
if int_rand_ans_2 != int_rand_ans_1 and str(term_list[int_rand_ans_2]) != str(picked_term):
ans_2_acceptable = True
ans_3_acceptable = False
while ans_3_acceptable == False:
int_rand_ans_3 = random.randint(1, len(term_list)) - 1
if int_rand_ans_3 != int_rand_ans_1 and int_rand_ans_3 != int_rand_ans_2 and str(term_list[int_rand_ans_3]) != str(picked_term):
ans_3_acceptable = True
#Decide if the correct answer is A, B, C, or D
correct_ans = random.randint(1,4)
#Print the options using the variables gathered above
if correct_ans != 1:
print "A) " + answer_list[int_rand_ans_1]
else:
print "A) " + terms_ans[picked_term]
if correct_ans != 2:
print "B) " + answer_list[int_rand_ans_2]
else:
print "B) " + terms_ans[picked_term]
if correct_ans != 3:
print "C) " + answer_list[int_rand_ans_3]
else:
print "C) " + terms_ans[picked_term]
if correct_ans == 1:
print "D) " + answer_list[int_rand_ans_1]
elif correct_ans == 2:
print "D) " + answer_list[int_rand_ans_2]
elif correct_ans == 3:
print "D) " + answer_list[int_rand_ans_3]
else:
print "D) " + terms_ans[picked_term]
print
Now, usually it outputs everything like you'd expect. I don't have a feature to automatically generate questions yet so I have to type in the line:
gen_question('term1')
or whatever term I'm using.
Here is the output I've been getting:
How many questions on your test? (There are 20 questions in total.) 20
>>> gen_question('term1')
Question 1 of 20:
term1
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
gen_question('term1')
File "C:\Users\Owner\Desktop\LEARNING PYTHON\scripts\in progress\Quizzer.py", line 69, in gen_question
int_rand_ans_1 = random.randint(1, len(term_list)) - 1
File "C:\Users\Owner\Desktop\LEARNING PYTHON\python 2.7.5\lib\random.py", line 241, in randint
return self.randrange(a, b+1)
File "C:\Users\Owner\Desktop\LEARNING PYTHON\python 2.7.5\lib\random.py", line 217, in randrange
raise ValueError, "empty range for randrange() (%d,%d, %d)" % (istart, istop, width)
ValueError: empty range for randrange() (1,1, 0)
>>> gen_question('term8')
This is what is getting you:
term_list = [...]
is defined at the start of your file, but later on you do the following when the amount entered is the max.
test_term = term_list
This does not create a copy of your array, this creates two variables which both reference the same array. So any further modifications to test_term are actually reflected against the list referenced by both variables.
And since you are defining test_terms at a global level in the script you NUKE it when you make this call
def list_scrambler(unscrambled_list):
test_terms=[]
countdown = len(unscrambled_list) + 1
for x in range(1, countdown):
transfer_var = random.randint(0,len(unscrambled_list)-1)
test_terms.append(unscrambled_list[transfer_var])
del unscrambled_list[transfer_var]
return test_terms
Also to add,
Hungarian notation is a big no-no and python is a strongly typed language anyways. If you are having a hard time keeping track of times don't rely on variable names. Instead get yourself an IDE or use names expressive of what they are doing.
if something == false:
should be rewritten as
if not something
This one is more for preference, but when printing out text that needs to have data floated in, you can save yourself some headache and write
"D) {0}".format(somelist[index])
This will stuff the variable into the {0} and provides you with some formatting context and prevents you from having to str() an object.
Also, globals in general are considered a bad thing, they're debatable. like globals in C sometimes they serve a clear purpose, but for the most part they hide bugs and make issues harder to track. Also sometimes your variable declarations will shadow globals, others (as you saw) will let you screw things up.
Well, it's pretty obvious that randint() is complaining because term_list is empty, right? Then
random.randint(1, len(term_list))
is
random.randint(1, 0)
and randint is stuck. So why is term_list empty? It's because this statement:
test_terms = list_scrambler(gen_test(int(raw_input("How many questions on your test? (There are " + str(len(term_list)) + " questions in total.) "))))
destroys term_list, and that's probably ;-) not intended.
It's hard to follow the code to track down why that happens. The basic problem is that gen_test can set
test_terms = term_list
and then end up returning term_list under the name test_terms. Then term_list is still intact at the start of list_scrambler, but empty by the time list_scrambler ends. The
del unscrambled_list[transfer_var]
deletes all the elements in term_list, one at a time.

Exhaustive Enumeration python

I need to create a program that finds the base and exponent of a single number given that the exponent is less than 7 and greater than 1. I am using python 2.7.
My code is as follows:
def determineRootAndPower(inputInteger):
pwr = 1
num = inputInteger
while (num) > 0 and (0 < pwr < 7):
inputInteger = inputInteger - 1
pwr = pwr + 1
num = num - 1
if int(num)**int(pwr) == inputInteger:
print(str(num) + (" to the power of ") + str(pwr) + (" equals ") + str(inputInteger) + ("!"))
else:
print("No base and root combination fit the parameters of this test")
Can anyone give me any general advice on this issue? Right now I am always receiving the 'else' statement which is not correct.
First, the reason you're always hitting the else is that you're doing the if check after the loop is over. So, instead of checking each value, you're just checking the very last values.
You want to print the "Yes" answer if any value matches, and the "No" only if all values fail. For that, you need to put the if inside the loop, and break as soon as you find the first success (unless you want to print all matches, instead of just the first one), and then the else becomes something you do only if you didn't find any of them.
You can use an else: with a while:, which gets run only if you didn't break anywhere. But many people find that confusing, so it might be simpler to just return instead of break on success, and just always print the failure message if you finish the loop.
Meanwhile, I think what you're hoping to do is handle all num values from inputNumber to 0, and, for each one, all pwr values from 1 to 7. To do that, you need a nested loop.
While we're at it, using a for loop is a whole lot easier than using a while loop around a variable that you initialize and +1 or -1 each time through.
Putting all of that together:
def determineRootAndPower(inputInteger):
for num in range(inputInteger, 0, -1):
for pwr in range(1, 7):
if int(num)**int(pwr) == inputInteger:
print(str(num) + (" to the power of ") + str(pwr) + (" equals ") + str(inputInteger) + ("!"))
return
print("No base and root combination fit the parameters of this test")
You can simplify this further.
What you really want is all combinations of any num in range, and any pwr in range. You don't care about how the nesting works, you just want all the combinations. In mathematical terms, you want to loop over the cartesian product of the two ranges. The function itertools.product does exactly that. So:
def determineRootAndPower(inputInteger):
for num, pwr in itertools.product(range(inputInteger, 0, -1), range(1, 7)):
if int(num)**int(pwr) == inputInteger:
print(str(num) + (" to the power of ") + str(pwr) + (" equals ") + str(inputInteger) + ("!"))
return
print("No base and root combination fit the parameters of this test")
As a side note, there are two things that make this code harder to read for no good reason.
First, if you want to print out an expression, it's a lot easier to use format (or %) than to manually convert things to strings and concatenate them together. Formatting lets you see what the output will look like, instead of having to figure it out, and it takes care of the stringifying and related stuff automatically.
Second, adding parentheses where they're not needed makes the code harder to read. The parentheses around your print expression makes your code look like Python 3, but it's actually Python 2. And the parentheses around each string inside the expression are even worse—at first glance, it looks like those are supposed to be inside the quotes. Even the parentheses in your test expression, (num) > 0 and (0 < pwr < 7), force the reader to pause—normally, parentheses like that are used to override the normal way operators combine together, so you have to think through what would be wrong with the normal num > 0 and 0 < pwr < 7 and how the parentheses make it different, only to eventually figure out that it's actually exactly the same.
Anyway, compare these two and see which one is easier to follow:
print "{} to the power of {} equals {}!".format(num, pwr, inputInteger)
print(str(num) + (" to the power of ") + str(pwr) + (" equals ") + str(inputInteger) + ("!"))

Categories

Resources