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)
Related
I am trying to build something simple from a book that I have. I want it to only accept one of two inputs, either Clockwise or Counterclockwise. However, the program enters the while loop even while they type Clockwise or Counterclockwise. Is there a way I can fix this?
import turtle
Spin = input("Clockwise or Counterclockwise?")
while Spin != "Clockwise" or Spin != "Counterclockwise":
print("Please Try Again")
Spin = {input("Clockwise or Counterclockwise?")}
x = input(str("Put in desired Length:"))
SideLength = int(x)
def drawSideAndTurn(SideLength):
turtle.forward(SideLength)
if Spin == "Clockwise":
turtle.right(90)
if Spin == "Counterclockwise":
turtle.left(90)
y = (input("Number of Sides:"))
n = int(y)
x = 1
while x <= n:
drawSideAndTurn(SideLength)
drawSideAndTurn(SideLength)
x += 1
SideLength += 10
I'm not really sure how to define it so that Spin could be equal to the string input of a user.
The issue isn't in the value in String; the issue is in the Boolean condition you're expressing.
Your while loop is framed as follows:
while Spin != "Clockwise" or Spin != "Counterclockwise":
print("Please try again!")
Let's walk through the evaluation for arbitrary inputs.
Suppose your user enters Clockwise.
Spin != "Clockwise" will evaluate to False
Spin != "Counterclockwise" is equivalent to "Clockwise" != "Counterclockwise", which is True
while False or True is while True, so the body of the while loop will evaluate.
Suppose instead your user enters Counterclockwise.
Spin != "Clockwise" will evaluate to True.
while True or [...] is while True, so the body of the while loop will evaluate (Python evaluates Boolean conditions lazily, so it won't even bother testing the part of the expression to the right of the or; see here).
If you want to validate the input to ensure it's in one of two values (Clockwise or Counterclockwise) you need to check something like:
while not (Spin == "Clockwise" or Spin == "Counterclockwise"):
...
or:
while Spin != "Clockwise" and Spin != "Counterclockwise"):
...
You can express this condition more concisely as:
while Spin not in ("Clockwise", "Counterclockwise"):
...
Better yet, if the set of inputs is known and reasonably fixed, you can write something like this:
from enum import Enum
class Spin(Enum):
CLOCKWISE = "clockwise"
COUNTERCLOCKWISE = "counterclockwise"
def __str__(self):
return self.value
input_spin = input("Enter direction of spin:")
# Loosely normalize input_spin so that "ClockwiSe "
# is treated like a valid input for "clockwise".
while not input_spin.strip().lower() in map(str, Spin):
...
Just change “or” to “and”. The input can’t be both be “clockwise” and “counter clockwise” at the same time, that’s why it never exits the while loop.
I want to search through a list of objects for the lowest number present in an attribute of the objects.
This is easy enough using a list comprehension and the min function. The issue comes when I want to find the index of the object.
class School:
def __init__(self, name, num_pupils, num_classrooms):
self.name = name
self.num_pupils = num_pupils
self.num_classrooms = num_classrooms
def students_per_class(self):
return self.num_pupils / self.num_classrooms
def show_info(self):
print(f"{self.name} has {self.students_per_class():.2f} students per class.")
def string_checker(question):
valid = False
while not valid:
try:
response = str(input(question))
if all(character.isalpha() or character.isspace() for character in response):
valid = True
return response
else:
print("Enter a string containing no numbers of special characters. ")
except ValueError:
print("Enter a string containing no numbers of special characters. ")
def num_checker(question):
valid = False
while not valid:
try:
response = int(input(question))
if (response):
valid = True
return response
else:
print("Enter an integer containing no letters or special characters. ")
except ValueError:
print("Enter an integer containing no letters or special characters. ")
def new_school():
school_name = string_checker("School Name: ")
num_pupils = num_checker("Number of Pupils: ")
num_classrooms = num_checker("Number of Classrooms: ")
return School(school_name, num_pupils, num_classrooms)
if __name__ == "__main__":
schools = []
school = School("Darfield High School", 900, 37)
schools.append(school)
school.show_info()
for i in range(1):
schools.append(new_school())
for school in schools:
school.show_info()
print(min(school.students_per_class() for school in schools))
# This works fine and prints the value of the lowest number
print(schools.index(min(school.students_per_class() for school in schools)))
# This doesn't work as it tries to find the index of the value returned
# from the min function as it should.
You can use min's key argument to search by index:
index = min(range(len(schools)), key=lambda i: schools[i].students_per_class())
print(schools[index])
A key provides the values that will be compared instead of the actual sequence. Here, my sequence is range(len(schools)), which is just the indices of all the elements. But instead of finding the minimum index, I am making it so that we find the minimum of schools[i].students_per_class() for each index i.
If you wanted to be able to compare, sort, find the min/ max on the items directly you could use the "dunder" methods. These methods allow you to overload built in functions on classes.
For instance if you had a class like this
class Item:
def __init__(self, value):
self.value = value
def __lt__(self, other):
return self.value < other.value
def __eq__(self, other):
return self.value == other.value
You can then create two instances and compare them directly like this,
A = Item(1)
B = Item(2)
print(A < B) # Prints True
or if you had a list of items
items = [A, B]
You can then get the minimum item by going
min_item = min(items)
or the index of it by going
min_item_index = items.index(min(items))
although it may be enough to just have a reference to the minimum item.
Use enumerate to iterate through lists while keeping track of indices:
min(((i, school.students_per_class()) for i, school in enumerate(schools)), key=lambda x: x[1])
(i, school.students_per_class()) is a tuple and using key parameter of min(), we ask Python to find minimum of school.students_per_class(), which subsequently returns it's index along with it.
Read about lambda here.
I think you may be looking for np.argmin function, if you provide a list as an input it will return the index of the minimal element.
in this case it would be:
import numpy as np
min_ind = np.argmin([school.students_per_class() for school in schools])
print(schools[min_ind])
#Tomas Ordonez
Your answer is here:
def minInt(instanceList):
sorted_instanceList = sorted(instanceList, key=lambda instance: instance.int)
minIndex = instanceList.index(sorted_instanceList[0])
return minIndex
I want to print whether a number is an anagram or not from the user input. I get an error saying nameError.
And this is my code for checking an input for an anagram.
n = input("Enter a long number")
factor = 2
factor_anagram = False
while factor < 10:
if isAnagram(n, factor):
print(n, "is an anagram with factor", factor)
factor_anagram = True
factor += 1
if not factor_anagram:
print("No")
Do I need to create a class? I tried creating an isAnagram class as well. But my implementation did not solve the issue.
You are trying to use isAnagram(n, factor) as a built in function like print("xyz"). isAnagram() is not a built in function, so you will have to define it at the top of your code. It can look like this:
def isAnagram(n, factor):
if (whatever comparison needs to be made here):
return True
else:
return False
Now when you call the isAnagram function, either True or False will be returned.
isAnagram does not exist in the default Python library - you have to define it yourself. From the way you're attempting to use it, you want to create a function
def is_anagram(n, factor):
# Your code here
# return True or False
You can then do something like if is_anagram(n, factor):.
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
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.