Generating a random number (more than once) and using it inside two class functions

Question:

I am building a GUI with tkinter where two buttons modify two text fields. One button chooses a random key from the example_dict and displays it in text1 , and the other reveals the corresponding value in text2 . I have chosen a OOP approach to organize my code, because it seems the easiest to read.

To give a more compact overview, I have boiled the script down to my problem. In the actual script I use a pandas dataframe instead of the example_dict , but for the purpose of this question, I wanted to maintain the possibility of copying and pasting .

import tkinter as tk
from random import randint

example_dict = {'a': "one", 'b': "two", 'c': "three"}

class MainApp:
    def __init__(self, master):
        self.master = master

        self.reveal_button = tk.Button(master, text="reveal", command=self.reveal)
        self.reveal_button.pack()
        self.reveal_button.place(relx=0.5, rely=0.25, anchor=tk.CENTER)
        
        self.random_button = tk.Button(master, text="randomize", command=self.random_word)
        self.random_button.pack()
        self.random_button.place(relx=0.5, rely=0.6, anchor=tk.CENTER)
        
        # create two text fields that will display the key:value pair from the dictionary
        self.text1 = tk.Text(master, height=2, width=20)
        self.text1.pack()
        self.text1.place(relx=0.3, rely=0.25, anchor=tk.CENTER)
        
        self.text2 = tk.Text(master, height=2, width=20)
        self.text2.pack()
        self.text2.place(relx=0.7, rely=0.25, anchor=tk.CENTER)

    def random_word(self):
        rand_num = randint(0,2)
        self.text1.delete(1.0, tk.END)
        self.text2.delete(1.0, tk.END)
        self.text1.insert(tk.END, list(example_dict.keys())[rand_num])

    def reveal(self):
        self.text2.delete(1.0, tk.END)
        self.text2.insert(tk.END, list(example_dict.values())[rand_num])

root = tk.Tk()
MainApp(root)
root.mainloop()

The problem is of course that rand_num is only defined within random_word , resulting in a NameError when I click the reveal_button. I am not sure where to generate the random number to be able to use the same number in both class functions.

I have tried a few things. First, I created a separate function that only generates a random integer, which I then called from both other functions. I did not expect this to work, because it generates different integers each time it is called, leading to mismatched key:value pairs. The attempt looked like this:

def random_number(self):
    return randint(0,2)

def random_word(self):
    rand_num = self.random_number()
    self.text1.delete(1.0, tk.END)
    self.text2.delete(1.0, tk.END)
    self.text1.insert(tk.END, list(example_dict.keys())[rand_num])

def reveal(self):
    rand_num = self.random_number()
    self.text2.delete(1.0, tk.END)
    self.text2.insert(tk.END, list(example_dict.values())[rand_num])

Second, I declared rand_num as global, which works, but everytime I’ve seen global variables being mentioned, people say they should be avoided, leading me to believe I should avoid them too.

def random_word(self):
    global rand_num
    rand_num = randint(0,2)
    self.text1.delete(1.0, tk.END)
    self.text2.delete(1.0, tk.END)
    self.text1.insert(tk.END, list(example_dict.keys())[rand_num])

Another idea I had and quickly discarded was generating the random number as an instance attribute, but that should just generate a number once, and I need it to generate a new number every time the random_button is clicked.

How do I generate a random number every time the random_button is clicked, that I can then use with both my functions?

Asked By: thelmo

||

Answers:

You can make a class field and use it this way:

def __init__(self):
    self.random_num = randint(0,2)

def abc(self):
    print(self.random_num )

def random_button(self):
    self.random_num = randint(0,2)

Answered By: Ilya Gorunov

maybe you can store last random value inside instance?

class MainApp:
    def __init__(self, master):
        self.master = master
        self.last_random = None
        ...

    def random_word(self):
            if not self.last_random:
                self.last_random = randint(0,2)
            self.text1.delete(1.0, tk.END)
            self.text2.delete(1.0, tk.END)
            self.text1.insert(tk.END, list(example_dict.keys())[self.last_random])

    def reveal(self):
        if not self.last_random:
            self.last_random = randint(0,2)
        self.text2.delete(1.0, tk.END)
        self.text2.insert(tk.END, list(example_dict.values())[self.last_random])
Answered By: toberon

For clarification, thanks to the suggestion from toberon, I came up with the following solution:

def __init__(self, master):
    self.master = master

    self.reveal_button = tk.Button(master, text="reveal", command=self.reveal)
    self.reveal_button.place(relx=0.5, rely=0.25, anchor=tk.CENTER)
    
    self.random_button = tk.Button(master, text="randomize", command=self.random_word)
    self.random_button.place(relx=0.5, rely=0.6, anchor=tk.CENTER)
    
    self.text1 = tk.Text(master, height=2, width=20)
    self.text1.place(relx=0.3, rely=0.25, anchor=tk.CENTER)
    
    self.text2 = tk.Text(master, height=2, width=20)
    self.text2.place(relx=0.7, rely=0.25, anchor=tk.CENTER)
    
    self.random_num = None

def random_word(self):
    self.random_num = randint(0, len(example_dict)-1)
    self.text1.delete(1.0, tk.END)
    self.text2.delete(1.0, tk.END)
    self.text1.insert(tk.END, list(example_dict.keys())[self.random_num])

def reveal(self):
    self.text2.delete(1.0, tk.END)
    self.text2.insert(tk.END, list(example_dict.values())[self.random_num])

Not sure if this is the most elegant way, but it does what it needs to do.

Answered By: thelmo