Tripzen
Tripzen

Reputation: 49

Is it possible to use the input() function to have the user input a variable for an object?

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

Answers (5)

tdelaney
tdelaney

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

shiv
shiv

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

Barmar
Barmar

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

Gwenn
Gwenn

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 KeyErrors.

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

DYZ
DYZ

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

Related Questions