Reputation: 49
Let's say I have a class and an object of said class:
class Rock:
def __init__(self, name, group):
self.name = name
self.group = group
granite = Rock('Granite', 'Igneous')
Now let's say I want the user to input a rock, so I have a line such as this:
rock = input('Type the name of a rock: ')
Based on what rock the user inputs, I want it to print out a string such as, "Granite belongs to the igneous group." I can achieve this string by typing:
print(granite.name + ' belongs to the ' + granite.group + ' group.')
However, this only gives the illusion of choice, as if the user types any rock besides granite, they will be puzzled as to why the only output is about granite. Ideally, I would like to be able to type something like:
print(rock.name + ' belongs to the ' + rock.group + ' group.')
Where no matter what the user inputs, if there's an object with a variable equal to that string, the rock variable will now be equal to said rock object. However, this doesn't work because the user is inputting a string, and not a variable class. I can get around this by adding an if statement such as:
if rock == 'granite':
rock = granite
Though, if I do that, then I would have to write an if statement for EVERY rock object I have, and if I have A LOT of rock classes, this would be very inefficient. So my question is, is there a way to have the user input an actual variable instead of just a string? If not, any advice on how to do this would be appreciated.
Upvotes: 3
Views: 194
Reputation: 77357
In another twist of the idea to build an index for rocks, this solution creates a second class for the index that also indexes by group - perhaps that's your next task. A difference here is that since rocks are not indexed by a class variable, you can potentially build different rock databases. Perhaps rocks from different regions? Who knows, but since is skips a global variable at class level, it is more flexible.
from collections import defaultdict
class Rock:
def __init__(self, name, group):
self.name = name
self.group = group
class RockIndex:
def __init__(self):
self.by_name = {} # one rock by name
self.by_group = defaultdict(list) # many rocks by type
def make_rock(self, name, group):
rock = Rock(name, group)
# normalize name a group to upper for index
self.by_name[name.upper()] = rock
self.by_group[group.upper()].append(rock)
return rock
rock_db = RockIndex()
rock_db.make_rock('Granite', 'Igneous')
rock = input('Type the name of a rock: ')
try:
grp = rock_db.by_name[rock.upper()].group
print(f"{rock} belongs to the {grp} group.")
except KeyError:
print("I don't know that rock")
Upvotes: 1
Reputation: 177
There's too much input validation required for what you propose, and even after that, I don't think that's the right approach.
Continuing @DYZ's answer (he posted while I was composing), you should create a dictionary and try to match the input against the dictionary with input handling too.
rocksDict = {
"rock1": Rock("Granite"),
"rock2": Rock("Igneous"),
"rock3": [Rock('Granite'),Rock('Granite')]
}
rockName = input('Type the name of a rock: ')
if key in rocksDict.keys():
#debugging comments
#print("Present, ", end =" ")
#print("value =", dict[key])
type = rocksDict.get(rockName)
print(type)
else:
print("Invalid Rock Name")
This way you can accommodate a multitude of rocks by just continue to filling rocksDict.
Upvotes: 1
Reputation: 781370
Put all the rocks in a dictionary so you can look them up by name.
class Rock:
all_rocks = {}
def __init__(self, name, group):
self.name = name
self.group = group
self.all_rocks[name] = self
granite = Rock('Granite', 'Igneous')
rock_name = input("Type the name of a rock")
rock = Rock.all_rocks.get(rock_name)
print(rock.group)
Upvotes: 2
Reputation: 534
Python has a way to retrieve all local variables in a dict: locals()
.
You could do sth like rock = locals()[input("...")]
Of course, you'd need to also handle KeyError
s.
But since this dict contains all of the local variables, as soon as you have another variable which is not an instance of Rock
, the user could select this irrelevant variable. You could also handle that case with isinstance(..., Rock)
But all of this is kind of "hacky", and using a dict with all stones mapped by name is much preferred and safe.
Upvotes: 1
Reputation: 57033
Elaborating @Barmar's suggestion. Use a dictionary:
rocks = {"granite": Rock('Granite', 'Igneous'), ....}
rock_name = input('Type the name of a rock: ')
rock = rocks[rock_name.lower()] # As long as it is a valid name
You may not even need Rock.name
anymore.
Upvotes: 4