Dealing with many similar functions in python

Question:

So, I’m programming a game in python that has a lottery system. To this I have a function called lottery which is played whenever the lottery is used. This lottery system get a random number from 1 – 50 and then calls 1 of 50 lottery event functions which are every so slightly different.

The problem is that this function is basically unreadable, as it defines 51 separated functions, and the lottery function uses a list of 50 different if states to check which outcome it should use.

I’ve tried rewriting this code I don’t know how many times now. First, I tried to refactor it using the built in typing module’s overload descriptor, but overload requires the parameters to be different types for every different overload. This just led to me adding 50 different type classes which was even less efficient.

I then tried calling the function from the builtin globals dictionary, but my IDE would flag an error when running, even if the code worked. here’s some pseudocode, since I no longer have the original code:

def lottery()
    lotnumb = random number(range(50))
    if lotnumb == 1:
        lotevent1()
    elif lotnumb == 2:
        lotevent2()
    elif lotnumb == 3:
        lotevent3()
    elif lotnumb == 4:
        lotevent4()
    elif lotnumb == 5:
        lotevent5()
    elif lotnumb == 6:
        lotevent6()
    elif lotnumb == 7:
        lotevent7()
    // this continues all the way to if statement number 50
Asked By: Kyle Naffin

||

Answers:

Passing function as an argument

def f_1():
    print("1")

def f_2():
    print("2")

def f_3():
    print("3")

def f_4():
    print("4")

def applyFunction(func):
    func()

if __name__=="__main__":
    f1= f_1
    applyFunction(f1)
Answered By: Wi Cheung

You could easily put all the functions into a list and index it but if you want something more complex or just dont want to use them in a list here is a solution.
This seems to me like a situation where you could use a the __getattribute__ method in python classes. The way you could do this is by creating a lottery_event class and within it define each of your functions.

It would look like this.

class LotteryEvents:
    @staticmethod
    def lotevent1():
        pass
    @staticmethod
    def lotevent2():
        pass
    @staticmethod
    def lotevent3():
        pass
    # and so on...

Then what you can do is use python’s dunder method __getattribute__ to be able to access each function. Calling __getattribute__ with the name of the function you want to use will return that function. You can add this as a method in the class like so.

class LotteryEvents:
    @classmethod
    def getLotEvent(cls, num):
        # gets the function and stores it to the variable loteventfunc
        loteventfunc = cls.__getattribute__(f'lotevent{num}')
        return loteventfunc
        # you could just call it from here
    @staticmethod
    def lotevent1():
        pass
    @staticmethod
    def lotevent2():
        pass
    @staticmethod
    def lotevent3():
        pass
    # and so on...

# then to call it you do this
random_num = random.randint(1, 50)
func = LotteryEvents.getLotEvent(random_num)
func()

There are many other ways to solve this problem, but this is how I would most likely tackle something like this. It does not remove having to define each function but it does remove that awful mess of else ifs and looks cleaner. If it still takes up too much space in your file I would recommend putting this class into a separate file and importing it into your project. Even without this solution putting it all in a different file could help organize your code.

Answered By: Shark Coding

Without knwoing more details about what is actually inside your loteventX functions, what I can suggest you is to add them into a list and randomly choose one and call, from inside you lottery function. Like this:

lottery_events = [
  lotevent1,
  lotevent2,
  lotevent3,
  lotevent1,
  ...
]                
def lottery():
    lotevent = random.choice(lottery_events)
    lotevent()

However, I think there might be a simpler solution where you can parameterize your loteventX functions and not need to implement each of them independently. Please share more details if you want to explore that option.

Answered By: Rodrigo Rodrigues
import random
def f_1():
    print("1")

def f_2():
    print("2")

def f_3():
    print("3")

def f_4():
    print("4")

def applyFunction(func):
    func()

if __name__=="__main__":
    f_list = [f_1,f_2,f_3,f_4]
    applyFunction(random.choice(f_list))
Answered By: Wi Cheung
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.