I'm fairly new to python (coding in general really), but I mostly grasp what's put in front of me. I'm trying to create an input that will only accept integers, so I put together the following function:
def askFor(request):
"""Program asks for input, continues to ask until an integer is given"""
num = ' '
while (isinstance(num, int) == False):
num = input(request)
else:
return num
it works fine when i enter a number, but anything else breaks it instead of looping it back. Am I missing an element, or am I just best off to go with
str(input('wargharble: '))
You are on the safer side with raw_input in Python 2, because it will always return a string, whereas input will try to evaluate the given string, which is dangerous.
def askFor(prompt):
""" ask for input, continues to ask until an integer is given
"""
number = None
while True:
try:
number = int(raw_input(prompt))
break
except ValueError:
pass # or display some hint ...
print(number) # or return or what you need ...
The problem is that input() returns the raw user input evaluated as a python expression. So when you input something like "someinput" on the console here is what happens:
The raw input is captured from the console as a string
That string is then parsed as a python expression (so imagine you simply type this string into your program, in this case, it becomes a variable with the name "someinput"
When isinstance() is called on this python expression, the python interpreter can't find a variable named "someinput" which is defined - so it throws an exception.
What you really want to do is use raw_input(), this will return a string representing the user's input. Since you are receiving a string from the console now though, you need some way of checking if a string is an int (because isinstance will always return a string)
Now to determine if the string is a number, you could use something like isdigit() - but be careful - this function will validate any string that contains only digits (so 0393 would be valid).
Here are the corrections that need to be made:
def askFor(request):
"""Program asks for input, continues to ask until an integer is given"""
num = ' '
while (num.isdigit() == False):
num = raw_input(request)
else:
return num
This is because input() is failing on non-integer input (this has to do with the fact that input() is calling eval() on the string your inputting (see python builtins).
raw_input() does not. (in fact, input() calls raw_input())
Try this instead:
while True:
num = raw_input(request)
if num.isdigit():
break
The isdigit() function checks to see if each character in a string is a digit ('0' - '9')
input evaluates whatever's typed in as Python code, so if the user types something non-sensical in, it'll produce an error. Or worse, it can run arbitrary commands on your computer. Usually, you want to use raw_input and convert the string value it returns safely yourself. Here, you just need to call int on the string, which can still produce an error if the user enters a non-int, but we can handle that by looping.
And voila:
def read_int(request):
while True:
try:
return int(raw_input(request))
except ValueError:
pass
Note, there's no need for break or else; just return the value once you have parsed it successfully!
Well first off, your indentation if a bit off (the while loop's indentation level does not match the previous level).
def askFor(request):
"""Program asks for input, continues to ask until an integer is given"""
num, isNum = '', False # multiple assignment
while not isNum:
num = input(request)
if type(num) in (int, long): # if it is any of these
isNum = True
break
else: isNum = False
return num # at the end of the loop, it will return the number, no need for an else
Another way to do it:
def askFor(request):
"""Program asks for input, continues to ask until an integer is given"""
num, isNum = '', False
while not isNum:
num = raw_input(request)
try: # try to convert it to an integer, unless there is an error
num = int(num)
isNum = True
break
except ValueError:
isNum = False
return num
Related
"Given a string of both letters and special characters/numbers, use recursion to concatenate the letters into a single string and return it."
My code is below, I'm still learning recursion and am stuck in trying to trace it. I tried a bunch of different lines in this code but idk how to fix what I do have so far:
def decoder(encryptedStr):
if len(encryptedStr) != 0:
if encryptedStr[0].isalpha() == True:
decoded = encryptedStr[0]
decoded.join(decoder(encryptedStr[1:]))
print(decoded)
else:
decoder(encryptedStr[1:])
I haven't had it return anything yet because I'm struggling with the part where I have to join the new letters to the output string. Instead of .join I also tried:
decoded += decoder(encryptedStr[1:])
but it doesn't work bc Nonetype??
Your main issue is that you didnt return, but there are some issues with your approach that make this more complex than need-be.
Think tail-first when doing recursion- What is your end condition, and how do you decide to continue. Typically with this kind of method you do something like, 1) process a single value in the list, 2) let the recursive method handle the rest of it, 3) combine the results.
An easy indicator of the tail-first return here would be to return nothing if the string is empty:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
...
Now in each run we want to operate on one letter and pass the rest to a recursive call. Ignoring the special character requirement, you'd get something like this:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
return first + rest
Now we can handle the special case where we want to omit letters.
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
if not first.isalpha():
first = ""
return first + rest
And that's all there is to it!
Bonus for some refactoring:
def clean(letter):
return letter if letter.isalpha() else ""
def decoder(encrypted):
if len(encrypted) == 0:
return ""
return clean(encrypted[0]) + decoder(encrypted[1:])
There's a bunch of problems here:
I don't think join does what you want it to do in that case. If you want to add some strings together simply use +=. join would insert decoded character between whatever decoder(encryptedStr[1:]) returns.
You don't have a case for len(encryptedStr) == 0, so it returns default value of None. That's why you cannot append it's results to decoded.
Return immediately if there is nothing to do. Otherwise take the first letter if it matches the condition and add the result of the recursive call (where the parameter is the current encrypted string without the first character).
def decoder(encrypted):
if not encrypted:
return ''
decrypted = encrypted[0] if encrypted[0].isalpha() else ''
return decrypted + decoder(encrypted[1:])
print(decoder('Abc123rtZ5'))
The result is AbcrtZ.
Bonus info (as #JonSG mentioned in the comments):
Run this with print(decoder('A' * 1000)) and you'll see why recursion is a bad idea for this task.
Every recursive function must have a base condition that stops the recursion or else the function calls itself infinitely.
To recursively concatenate ONLY the letters of an input string into a single output string:
some_string = "I2L4o2v3e+P;y|t!o#n"
def decoder(encryptedStr, decoded = ""):
if len(encryptedStr) == 0: # Base condition
return decoded
if encryptedStr[0].isalpha():
decoded += encryptedStr[0]
return decoder(encryptedStr[1:], decoded)
# If the char in the index [0] is not a letter it will be sliced out.
return decoder(encryptedStr[1:], decoded)
print(decoder(some_string))
Output:
ILovePython
I have written the function below. It will work if I pass it an integer or string: If I pass an integer, the program will calculate the factorial, and if I pass a string, it will check the type with isinstance and will print the error message.
But, if I pass a float number or a negative number, the program will print an error... in my second condition. I have said that if n is less than 0, then print that error message, but apparently it won't work, and I don't know why.
def factorial(n):
if not isinstance(n,int):
print('Factorial is only defined for integers.')
return(None)
elif n<0:
print('Factorial is not defined for negative integers.')
return(None)
elif n==0:
return(1)
else:
return(n*factorial(n-1))
How can I change the program so that my conditionals work for float and negative numbers? I don't want to use try and except.
I've been learning Python for about 3 weeks now, so what I'm working on is obviously going to be very simple. I've already written a couple programs that return values for certain calculations, and I have successfully used exceptions within functions in those programs. The program that I'm currently working on is only for the purpose of me getting some more practice writing functions, while loops, and exceptions. Here's what I've got so far:
def factorial(n):
while n > 0:
return n * factorial(n-1)
return 1
print factorial(n)
def user():
n = int(raw_input("What number would you like to use?"))
try:
factorial(int(n))
except ValueError:
print "You must choose a number. Please try again."
return user()
if __name__ == '__main__':
user()
I can't figure out why my exception within the user() function doesn't seem to be running. All I get when I run this code is the raw_input "What number would you like to use?". The factorial() function doesn't run, nor do the print or return statements that I've set if there is a ValueError. Can anyone help me figure out why this is (or rather isn't) happening?
Funny enough, all of the code is running! It just isn't showing anything, because the print statement isn't being called! Lets decompose the user function and see what it does.
What the code is doing
n = int(raw_input("What number would you like to use?"))
Convert the string the user types into an integer and store it into the variable (or, if you want to be pythonic-ly correct, the name) n.
try:
factorial(int(n))
Try to convert n into an integer (again) and send said integer to the factorial function. Note that n here is already an integer! So this will always succeed. Just call the function, nothing more.
except ValueError:
print "You must choose a number. Please try again."
return user()
If ValueError is raised, print an error message, then return the value of user.
We don't print anything whether the conversion was successful or not. We just run a single function then exit. What function do we run?
def factorial(n):
while n > 0:
return n * factorial(n-1)
return 1
print factorial(n)
Note that in this function the code says to print a value after returning a value. Once a function returns, nothing after the return statement is run. The print statement will not be executed! So this function correctly computes the factorial (recursively), and returns it. Nothing else!
This is why your program will seem to do nothing. This function is called, computes, and returns the answer. The returned answer is simply ignored!
What does this mean?
There are three key lines in your code that define why it's doing what it's doing.
You convert the input from the user into an integer and store it in n
This will raise ValueError if the user types anything that isn't an integer.
Note that this still happens! if you run the code, type b, then press enter, an exception (ValueError) will be raised.
You convert n into an integer and pass it to factorial.
Although this is in the try block (as it should be), n is already an integer. This is why your exception code will never run.
Your only print statement (after getting valid input from the user) is inside the factorial function, after the return statement. It will never be executed. This is why nothing is printed.
I don't recommend printing the result inside of a recursive function. It can be done, but it is generally better to return the result and print the returned value, outside of the recursive function. (perhaps in the user function, instead of calling factorial(n) printing what it returns would be better!)
I hope that this helped to clarify exactly what is going on with the code, and I hope I was able to answer your question! Happy coding! :D
Here are my comments expanded into an answer.
First, do not print inside the factorial function. Print the value returned from that function:
def factorial(n):
while n > 0:
return n * factorial(n-1)
return 1
>>> factorial(4)
24
Next, wrap the thing that you think will cause the exception in the try part of the block. In this case, the call to int with unknown user input might cause an exception; wrap that in the try -- no more. Finally, do not use recursion for user input. Use a while loop instead:
def user():
n=None
while not n:
user_int=raw_input("What number would you like to use? ")
try:
n=int(user_int)
except ValueError:
print "You must choose a number. Please try again."
print factorial(n)
if __name__ == '__main__':
user()
first of all you have to print the factorial(int(n)) to see the output,
And remove int conversion of your input,
here is my modified code:
def factorial(n):
2 while n > 0:
3 return n *factorial(n-1)
4 return 1
5 print factorial(n)
6
7
8
9
10 def user():
11 n = raw_input("What number would you like to use?")
12
13 try:
14
15 print factorial(int(n))
16 except ValueError:
17 print "You must choose a number. Please try again."
18 return user()
19
20 if __name__ == '__main__':
21 user()
hope this helps,
I am trying to validate input using Python for simple fields.
map_x of type int
map_y of type int
obstacles of type bool
I have tried many of the suggestions here on SO. My code:
class Map(object):
def __init__(self):
# Note: These are defaults.
# map_x and map_y both have a range of 20 <= x <= 100
# obstacles = 'Y' || 'N'
self.map_x = -1
self.map_y = -1
self.obstacles = '-'
def forest_map(self):
self.map_x = self.choice("Map Width")
self.map_y = self.choice("Map Length")
self.obstacles = self.choice("Obstacles (Y/N)")
def choice(self, text):
# Code
I have tried several different solutions at an attempt at staying true to the concept of "efficient" and "re-usable" code.
The reason for the text argument found in method 'choice': I have made prompts that indicate what the user is interacting with (e.g. choice = raw_input(text + "-> ")).
My solutions:
I have tried testing with if statements for the text argument, but that, to me, is too particular to this solution; thus, not re-usable.
I have tried using try / except, but, even still, it seems that my while statement is consuming input and is not returning a value to map_x and map_y.
I have tried (successfully! in a previous section for menu selection, not gathering user preferences) using a dictionary as follows:
# menu is an argument that takes a dictionary.
# dictionary can be found in a list of dictionaries named menu_list.
# choice is a member of class Menus, and menu_list is defined in Menu's __init__.
def choice(self, menu):
acceptable = menu
if menu == self.menu_list[2]:
choice = raw_input("Choice-> ")
while choice not in acceptable:
choice = raw_input("Choice-> ")
Map().generate(choice)
I was only successful in testing for 'obstacles'. I used (while choice not in ['Y', 'y', 'N', 'n']: #Code
As of right now, I am only having issues with testing for integers WHILE keeping true to the aspect of re-usability and efficiency.
Is there a way I can see if the input (choice) contains letters of any kind and, thus, request for more input?
Is there a way I can, simultaneously, ensure that map_x / map_y are within the range 2 <= choice <= 100?
Thanks a bunch,
Akratix
=== EDIT 10/2/14 ===
I have come up with two implementations thanks to the solutions below.
In order to to validate input that is supposed to be an integer as well as within a set range, I use the following code snippet:
def map_choice(self, text):
is_valid = False
while not is_valid:
try:
value = int(raw_input(text + "-> "))
if 2 > value or 100 < value:
print "Invalid input!"
continue
return value
except ValueError:
print "Invalid input!"
In order to to validate input that is supposed to be a particular letter in a "list" of acceptable input, I use the following code snippet:
def obstacle_choice(self, text):
is_valid = False
while not is_valid:
value = raw_input(text + "-> ")
if any(value == x for x in ('Y', 'N', 'y', 'n')):
return value
You're off to a great start, but don't go overboard trying to generalize everything. You have two vastly different fields with different validation, so it makes sense to use two different "choice" functions.
It will be much simpler to use something like the following. You can even customize the fields error messages to tell the user why his input is invalid and be more helpful in general.
def forest_map(self):
self.map_x = self.map_choice("Map Width")
self.map_y = self.map_choice("Map Length")
self.obstacles = self.obstacle_choice("Obstacles (Y/N)")
def map_choice(self, message)
# here we use try except to make sure its an integer
try:
value = int(raw_input(message))
except ValueError:
print "Invalid input!"
return -1
# check range
if 20 > value or 100 < value:
print "Invalid input!"
return -1
return value
def obstacle_choice(self, message):
value = raw_input(message)
# check if its an acceptable answer
if any( value == x for x in ('Y', 'N', 'y', 'n') ):
return value
print "Invalid input!"
return -1
Alternately, if you have a lot of fields, it might be worthwhile to have one choice function that takes a validation function as an argument. Similar to your second choice function, but instead of taking a "menu" dict, we take a validation function.
def forest_map(self):
valid_map = lambda x: 20 <= int(x) <= 100
valid_obstacle = lambda x: any( x == y for y in ('Y', 'N', 'y', 'n') )
# the choice function will guarantee its safe to use int on the result
self.map_x = int(self.choice("Map Width", valid_map))
self.map_y = int(self.choice("Map Length", valid_map))
self.obstacles = self.choice("Obstacles (Y/N)", valid_obstacle)
def choice(self, message, validate):
"""
Prompt a user for input and validate the input
Args:
message (str): Message to display at prompt
validate (callable): callable of one parameter to validate input
Returns:
str: A valid user input
"""
is_valid = False
while not is_valid:
# prompt for input
value = raw_input(message)
# validate input with our validation function
# if validate(value) returns True, it ends the loop
try:
is_valid = validate(value)
# maybe the validation function throws an exception
# its best to catch the specific error class, but I'll use Exception for simplicity
except Exception:
is_valid = False
return True
Finally, as to checking if an input contains any letters. You can use the isalpha method, which checks if every character in a string is a letter. To check if any char in a string is a letter you can use this.
value = raw_input('>')
contains_letters = any( c.isalpha() for c in value )
Give the choice function an additional argument, which I usually call type (despite the collision with a builtin), that is a callable that will transform and validate the argument.
Use this example like IntRange(2, 100)
class IntRange(object):
def __init__(self, min, max):
self.min = min
self.max = max
def __call__(self, s):
i = int(s) # throws
if self.min <= i <= self.max:
return i
raise ValueError('%d is out of range' % i)
More types I've implemented are here: https://github.com/o11c/attoconf/blob/master/attoconf/types.py ; the enum type may be useful for y/n answers (but you might want something that does case normalization as well)
I have a function that returns two different types (bool and int):
def limit_reached():
rate_limit = api.GetRateLimitStatus()
if not rate_limit['remaining_hits'] == 0:
return False
else:
return rate_limit['remaining_hits']
Basically, I want to be able to call the function to see if the "limit" limit has been reached. If it has not, before continuing, print how many 'remaining_hits' are left.
So far I have this, but can't figure out how to efficiently show 'remaining_hits' without calling the GetRateLimitStatus() function again:
if limit_reached():
print "Limit reached. Exiting"
exit()
else:
##Print remaining hits
##Continue...
Also, if it helps, I am using the Twitter API for Python
In python, the integer '0' is identically equivalent to 'False'. So, for any and every truth test in Python, the integer '0' evaluates to false.
For what I see, you can adapt your code to use this fact.
Hope it helps.
Store the result in a variable?
remaining = limit_reached()
if not remaining:
print "Limit reached."
else:
print "Remaining:", remaining
P.S. you can also return 0 when the limit was reached... No need to return False when it actually means 0.
You should redefine your limit_reached function:
def limit_reached():
return api.GetRateLimitStatus()['remaining_hits']
then something like:
remaining = limit_reached()
if remaining: # or 'remaining > 0' if you want to be explicit
##Print remaining hits
##Continue...
else:
print "Limit reached. Exiting"
exit()
As a commenter pointed out, returning different variable types is bad style. It is fairly easy to always return a boolean value, like so:
def limit_reached():
rate_limit = api.GetRateLimitStatus()
return rate_limit['remaining_hits'] == 0
rate_limit['remaining_hits'] == 0 is a complete statement that will return a 'true' or 'false' value, the result of which you can return from your function. Note that you do need to use two equals signs.
If you need the integer value itself, you can always return that instead, and test the condition in your other code, like so:
def limit_reached():
rate_limit = api.GetRateLimitStatus()
return rate_limit['remaining_hits']
if limit_reached() == 0:
print "Limit reached. Exiting"
exit()
else:
##Print remaining hits
##Continue...
Or, you could take advantage of the fact that all numbers (integer, float, double, it doesn't matter) with an exact value of 0 are treated as false (other constants treated as false include [], (), '', or '{}' - see here).
if limit_reached():
##Print remaining hits
##Continue...
else:
print "Limit reached. Exiting"
exit()
Note that the clauses have been reversed in this case.