Search code examples
pythondictionarysetvalue

Trying to set a variable from a value in a dictionary


I'm learning python and am creating a rock paper scissors game.

I'm stuck on one part.

I have 4 variables currently (Although I would like to get it down to 2)

  • pKey
  • pChoice
  • comKey
  • comChoice

They look up the the Key and Value respectively in a dictionary.

choice = {1:'rock',  2:'paper',  3:'scissors'}

The problem I'm having is getting the keys from the dictionary using the variables.

Here's the code snippet that's giving me trouble

    print('--- 1 = Rock    2 = Paper     3 = Scissors --- ')
    pKey = input() # this is sets the key to the dictionary called choice
    while turn == 1: # this is the loop to make sure the player makes a valid choice
        if pKey == 1 or 2 or 3:
            pChoice = choice.values(pKey)  # this calls the value from choice dict and pKey variable
            break
        else:
            print('Please only user the numbers 1, 2 or 3 to choose')

    comKey = random.randint(1, 3)  # this sets the computer choices
    comChoice = choice.values(comKey)

Specifically the troublesome part is

 pChoice = choice.values(pKey)

and

 comChoice = choice.values(comKey)

I've tried everything I know from using brackets, trying different methods, and using different formats.

Would love to learn this! Thanks!


Solution

  • Sounds like you're just trying to do a dictionary lookup

    pKey = 1
    pChoice = choices[pKey]  # rock
    

    dict.values is used to create a list (actually a dict_values object) with all the values of the dictionary. It's not used as a lookup.


    As far as your code structure goes, it could use some work. The rock/paper/scissors choice is perfect for an Enum, but that might be a bit beyond you right now. Let's just try as a toplevel module constant.

    ROCK = "rock"
    PAPER = "paper"
    SCISSORS = "scissors"
    
    def get_choice():
        """get_choice asks the user to choose rock, paper, or scissors and
        returns their selection (or None if the input is wrong).
        """
        selection = input("1. Rock\n2. Paper\n3. Scissors\n>> ")
        return {"1": ROCK, "2": PAPER, "3": SCISSORS}.get(selection)
    

    Addressing them as constants makes sure they're the same everywhere in your code, or else you get a very clear NameError (instead of an if branch not executing because you did if comChoice == "scisors")


    A minimal example with an enum looks like:

    from enum import Enum
    
    Choices = Enum("Choices", "rock paper scissors")
    
    def get_choice():
        selection = input(...)  # as above
        try:
            return Choices(int(selection))
        except ValueError:
            # user entered the wrong value
            return None
    

    You could extend this by using the more verbose definition of the Enum and teach each Choice instance how to calculate the winner:

    class Choices(Enum):
        rock = ("paper", "scissors")
        paper = ("scissors", "rock")
        scissors = ("rock", "paper")
    
        def __init__(self, loses, beats):
            self._loses = loses
            self._beats = beats
    
        @property
        def loses(self):
            return self.__class__[self._loses]
    
        @property
        def beats(self):
            return self.__class__[self._beats]
    
        def wins_against(self, other):
            return {self: 0, self.beats: 1, self.loses: -1}[other]
    
    s, p, r = Choices["scissors"], Choices["paper"], Choices["rock"]
    s.wins_against(p)  # 1
    s.wins_against(s)  # 0
    s.wins_against(r)  # -1
    

    Unfortunately there's no great way to lose the abstraction in that (abstracting out "paper" to Choices.paper every time it's called) since you don't know what Choices["paper"] is when Choices.rock gets instantiated.