Home > Back-end >  How do I randomly call a function from a list?
How do I randomly call a function from a list?

Time:06-29

I'm a Python beginner, I took a month break and today I'm back. In the book that I'm reading to learn Python the author asks us to memorize truth tables. I wanted to make it more fun, so I came up with the idea to try to write a script that asks me random questions (I wrote) from a list, which I can answer to with "true" or "false". The problem is that a lot of the things I used in the code I haven't done from the book yet, so I used the internet to help me.

This is what I came up with:

print("Here are some questions for you, answer true or false.")
input("Press 'Enter' if you would like to continue.")

def a():
    answer = input("1) not False: ")
    print("Correct!") if answer == 'true' else print("Wrong!")

def b():
    answer = input("2) not True: ")
    print("Correct!") if answer == "false" else print ("Wrong!")

def c():
    answer = input("3) True or False: ")
    print("Correct!") if answer == "true" else print ("Wrong!")

import random

questions = [a(), b(), c()]

print(random.sample(questions, 3))

The questions are a lot more, I've used only three just to test if the code was working. So far so good, if it weren't for the fact that I can't seem to call random functions from my list.

This is my output: output

Also, what does [None, None, None] mean?

I know it's not a good idea to use things that I haven't even studied yet, but I was very excited about this.

TIA!

CodePudding user response:

The problem

Using parens calls the function. What you need to do is store references to the functions: questions = [a, b, c].

Since None is returned by your functions implicitly (due to lack of explicit return; see this post), and because you merely called the functions when creating the questions list, all you did was answer each question (returning None), and shuffle the answers around.

Solution 1

By storing references to your functions in a questions list, this allows you to simply call the functions in a for loop iterating over the shuffled list:

questions = [a, b, c]
for q in random.sample(questions, 3):
    q()

Solution 2

You can modify your function to return points, which would allow you to start tracking your performance:

# Import statements should always be placed at the top of the file
import random


print("Here are some questions for you, answer true or false.")
input("Press 'Enter' if you would like to continue.")  # this does nothing


def a():
    answer = input("1) not False: ")
    print("Correct!") if answer == "true" else print("Wrong!")
    return int(answer.lower() == "true")


def b():
    answer = input("2) not True: ")
    print("Correct!") if answer == "false" else print ("Wrong!")
    return int(answer.lower() == "false")


def c():
    answer = input("3) True or False: ")
    print("Correct!") if answer == "true" else print ("Wrong!")
    return int(answer.lower() == "true")


questions = [a, b, c]
earned_points = [q() for q in random.sample(questions, 3)]

Solution 3

Even better, though, would be to use the if __name__ == "__main__" pattern:

import random


def a():
    answer = input("1) not False: ").lower()
    print("Correct!") if answer == "true" else print("Wrong!")
    return int(answer == "true")


def b():
    answer = input("2) not True: ").lower()
    print("Correct!") if answer == "false" else print ("Wrong!")
    return int(answer == "false")


def c():
    answer = input("3) True or False: ").lower()
    print("Correct!") if answer == "true" else print ("Wrong!")
    return int(answer == "true")


if __name__ == "__main__":
    print("Here are some questions for you, answer true or false.")
    proceed = input("Press 'Enter' if you would like to continue.")
    if proceed == '':
        questions = [a, b, c]
        earned_points = [q() for q in random.sample(questions, 3)]
        # now do something with earned_points, for example:
        # calculate and print the total points scored
        # calculate and print the user's 'grade' (their score out of the total)
        # calculate and print the average score
    else:
        print("You declined to play")

Further Improvements

There are numerous other improvements you can make to your program as well.

Note that the user's input is immediately converted to lowercase in the third solution -- this simplifies input validation. You could even write a helper function which complains if the user enters anything but 'true' or 'false'.

Something else thing to notice is that the code in your three functions is identical save for the question prompt and the answer. One thing you could do is implement a generic "question asker" function which takes two arguments: a prompt, and the expected answer. Then you'd store your questions as 2-tuples like ("true or false", "true"), or ("true and false", "false"), etc. Then you'd shuffle the question tuples then iterate over the shuffled list, passing them to the generic question asker.

CodePudding user response:

Let's decode what is happening in your program.

questions = [a(), b(), c()]

This list is calling three functions and storing their results. As you see, none of your functions are returning anything. This makes the contents of the above created list to be all None.

But why are you seeing a list containing three None values in your output?

That is because of print(random.sample(questions, 3)) . This line is trying to get three random values from a list that has all None values.

Now how to ask random questions or in your case, how to call random functions?

This can be done in multiple ways. One easy way I can think of at the moment is:

Make sure to modify your question list to:

questions = [a, b, c]

First create a random question number in the range of length of questions:

question_num = random.randint(0,len(questions)-1)

Then use the question number to call the appropriate question:

questions[question_num]()

This should ask one random question at a time. But if you want to keep asking a random question , you can call the above two lines in a loop.

This answer is in line with what your solutions is, but if you want to do the same thing, there are different efficient ways. I will try to add them here.

CodePudding user response:

Build a list of 2-tuples each of which containing the question and the correct answer. Then your program becomes very concise:

import random

questions = [('not False', 'true'),
             ('not True', 'false'),
             ('True or False', 'true')
             ]

for i, (q, a) in enumerate(random.sample(questions, k=len(questions)), 1):
    answer = input(f'{i}) {q}: ')
    print("Correct!" if answer == a else "Wrong!")

CodePudding user response:

I would suggest using lower() function to convert the answer provided by the user to a lowercase string where all characters are lowercase, independently of the input. Also, using list comprehension in combination with random.choice() to avoid getting [None, None, None] as part of the input can improve the result:

print("Here are some questions for you, answer true or false.")
input("Press 'Enter' if you would like to continue.")

def a():
    answer = input("1) not False: ").lower()
    print("Correct!") if answer == 'true' else print("Wrong!")

def b():
    answer = input("2) not True: ").lower()
    print("Correct!") if answer == "false" else print ("Wrong!")

def c():
    answer = input("3) True or False: ").lower()
    print("Correct!") if answer == "true" else print ("Wrong!")

import random

questions = [a, b, c]

for i in range(1):
    random.choice(questions)()

Output:

Here are some questions for you, answer true or false.
Press 'Enter' if you would like to continue.
1) not False: true
Correct!
  • Related