6

So I'm trying to setup a multiple choice quiz via Python. I'm fairly new to Python, so my apologies up front if there is a simpler way to do this. However, I'm trying to really understand some basics before moving forward to newer techniques.

I have a dictionary. In this dictionary, I want to grab 3 random keys. I also want to make sure that these three keys are not equal (in other words, random from one another). Here is the code I have wrote so far:

import random

word_drills = {'class': 'Tell Python to make a new kind of thing.',
               'object': 'Two meanings: the most basic kind of thing, and any instance of some thing.',
               'instance': 'What you get when you tell Python to create a class.',
               'def': 'How you define a function inside a class.',
               'self': 'Inside the functions in a class, self is a variable for the instance/object being accessed.',
               'inheritance': 'The concept that one class can inherit traits from another class, much like you and your parents.',
               'composition': 'The concept that a class can be composed of other classes as parts, much like how a car has wheels.',
               'attribute': 'A property classes have that are from composition and are usually variables.',
               'is-a': 'A phrase to say that something inherits from another, as in a Salmon *** Fish',
               'has-a': 'A phrase to say that something is composed of other things or has a trait, as in a Salmon *** mouth.'}

key1 = ' '
key2 = ' '
key3 = ' '             

def nodupchoice():
    while key1 == key2 == key3: 
        key1 = random.choice(word_drills.keys())
        key2 = random.choice(word_drills.keys())
        key3 = random.choice(word_drills.keys())


nodupchoice()

print "Key #1: %s, Key #2: %s, Key #3: %s" % (key1, key2, key3)

I'm fairly sure the issue is with my while loop. I wanted to create a function that will keep running until all three keys are different from one another. Finally, it would print the result. Any ideas? Thanks in advance.

kiddsupreme
  • 115
  • 1
  • 3
  • 13
  • Just to help improve your question: you refer to an "issue" but it's not clear what happens when you run your code. – Argalatyr Aug 18 '12 at 21:05

6 Answers6

12

You can use random.sample:

>>> random.sample(word_drills, 3)
['has-a', 'attribute', 'instance']

and you don't need .keys(), iteration over a dictionary is over the keys.

Note that random.sample will return three unique values from the list you supply (i.e. it will never return 'has-a' twice):

>>> all(len(set(random.sample(word_drills, 3))) == 3 for i in range(10**5))
True
DSM
  • 342,061
  • 65
  • 592
  • 494
  • Wow, now I'm feeling a bit simple-minded. Definitely streamlines what I was trying to do. I do have a follow up question though. Although it returns the information I wanted, how do I have the result assigned to the variables key1, key2, & key3? – kiddsupreme Aug 18 '12 at 21:18
  • 2
    I'd suggest you work with the list of keys itself, and simply use `keys = random.sample(word_drills, 3)`, and access them by `keys[0]` and `keys[1]` and `keys[2]`. 90% of the time when you use numeric suffixes in variable names it's a sign that you really want a list or tuple or dict. But if you want three separate names, you could simply write `key1, key2, key3 = random.sample(word_drills, 3)`. – DSM Aug 18 '12 at 21:22
  • Awesome. Thanks DSM! Just one more quick question. Lists are mutable correct? So if I were to run this over and over again, the items in the list should change as well? – kiddsupreme Aug 18 '12 at 21:26
  • Yes, the items will be (in general; could get repeats by chance) different each time. List mutability is something different, though, that simply means you can write `somelist[2] = 18` and change what `somelist[2]` refers to. You can't do that for tuples. – DSM Aug 18 '12 at 21:29
  • I guess I did have another question now that I'm thinking about this. Eventually, I wanted my program to not only grab 3 random keys, but to also grab one of the corresponding value to the "correct" answer. Basically, the program would provide one of the keys "values" as the question. Then the user would choose between the 3 random keys. If the key chosen matches up with the value in the dictionary, the user would be correct. Otherwise, the answer would be wrong. Does that make sense? – kiddsupreme Aug 18 '12 at 21:39
  • Comment sections aren't the best place for running conversations. You know how to map keys to values -- `word_drills[key1]` is the value corresponding to `key1`, for example -- so you can give that a try. You might find reading through the dictionary section of the official tutorial helpful. It shouldn't be too bad. :^) Good luck! – DSM Aug 18 '12 at 21:44
  • Thanks again DSM, appreciate it! – kiddsupreme Aug 18 '12 at 21:56
  • For Python3 you need to convert it to a list first: `random.sample(list(word_drills), 3). ` – erik Aug 25 '16 at 12:08
3

Use random.sample

>>> import random
>>> random.sample([1,2,3,4,5,6], 3)
[4, 5, 2]
applicative_functor
  • 4,926
  • 2
  • 23
  • 34
0

Probably the easiest thing to do is to shuffle the keys:

keysShuffled = list(word_drills)
random.shuffle(keysShuffled)

Then take the first 3

threeUnique = keysShuffled[:3]
Doug T.
  • 64,223
  • 27
  • 138
  • 202
  • `shuffle` works in-place and returns None, so this won't quite work as written. `keys = list(word_drills); random.shuffle(keys)` would work, though. – DSM Aug 18 '12 at 21:08
0

Maybe you would like to try Quiz Me 2.5? It is a multiple choice quizzing system with a GUI in Python 3.x.

Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
0

As others have explained, random.sample is the best way to do what you want.

To explain why your original code didn't work:

First problem, there is a logic error with your while loop - it will end as soon as two items are different (e.g 'a' == 'a' == 'b' is True, and the loop will end), to fix this, there are a few ways:

while not (key1 != key2 != key3):

Alternatively, a set can only contain unique items, so when the length of set([key1, key2, key3]) is 3, all three values are different:

while len(set([key1, key2, key3]) != 3:

Second problem, and the reason the code will refuse to run:

key1 key2 key3 are defined as global variables (key1 = ' ' are at the top-level of your.py` file, not in a function)

You can look up a global variable in a function without issue (like where you done while key1 == key2 ...). The error occurs because you also try and assign to key1 within your loop - this confuses Python's parser, because you are looking up a global variable in the while key1 == ... part, but trying to make a new function-local variable on the next line, so it helpfully throws an error.

A rubbish way to fix this is like so:

key1 = ' '
key2 = ' '
key3 = ' '             

def nodupchoice():
    global key1
    global key2
    global key3
    while not (key1 != key2 != key3):
        key1 = random.choice(word_drills.keys())
        key2 = random.choice(word_drills.keys())
        key3 = random.choice(word_drills.keys())

Then it will work as you intended, but don't do that - global variables have their uses, but this is not such a situation..

Instead of using a global variable, you can easily return multiple values from a function:

def nodupchoice():
    key1 = ' ' # local variables, which are inaccessible outside this function
    key2 = ' '
    key3 = ' '

    while not (key1 != key2 != key3):
        key1 = random.choice(word_drills.keys())
        key2 = random.choice(word_drills.keys())
        key3 = random.choice(word_drills.keys())

    return key1, key2, key3


key1, key2, key3 = nodupchoice()

print "Key #1: %s, Key #2: %s, Key #3: %s" % (key1, key2, key3)

Oh, and even better, you can pass in the word_drills as an argument:

def nodupchoice(things):
    key1 = ' ' # local variables, which are inaccessible outside this function
    key2 = ' '
    key3 = ' '

    while not (key1 != key2 != key3):
        key1 = random.choice(things)
        key2 = random.choice(things)
        key3 = random.choice(things)

    return key1, key2, key3


key1, key2, key3 = nodupchoice(word_drills)

...and you've written a function almost identical to random.sample(word_drills, 3)!

Community
  • 1
  • 1
dbr
  • 165,801
  • 69
  • 278
  • 343
  • There's a logic error in the original code, though. The while loop will end as soon as it's not true that the three keys are the same, which isn't the same thing as the three keys being different. It'll stop trying if two are the same and the third differs, for example. – DSM Aug 18 '12 at 21:38
  • 1
    `'a' == 'a' == 'b'` is *not* True. Also, `while not (key1 != key2 != key3):` is *not* a fix (`key1` may be equal to `key3`). – Reinstate Monica Apr 09 '14 at 22:30
-1

I had this samme problem and wrote https://github.com/robtandy/randomdict to solve it. Maybe it will help you also.

It provides a python dict object but with the additional methods, random_key, random_value and random_item. All with O(1) run time complexity.

Rob T
  • 383
  • 4
  • 3