Irfan Sindhi
Irfan Sindhi

Reputation: 75

Not able to call a function within a function

I am a beginner in python. I am making a Toss Simulator. This is my code:

import random

class TossSimulator():

    heads = 0
    tails = 0

    def doFlip(self):
        x = random.randint(0,1)
        if(x == 0):
            return True
        else:
            return False


    def getScore(self, flip_function):
        if flip_function is True:
            self.heads+=1
        else:
            self.tails+=1   

obj = TossSimulator()

obj.getScore(doFlip())          

The error I am recieving is :

NameError : name 'doFlip' is not defined at line 24

Upvotes: 0

Views: 57

Answers (1)

Engineero
Engineero

Reputation: 12948

You really do not need a class for this, but if you just want to practice, then practice well! The problem is, as stated in the comments, that doFlip does not exist in the scope in which you are trying to call it. Wrapping some of the comments up into a solution that illustrates some nice features:

import random

class TossSimulator():
    def __init__(self, flip_function):
        """
        Args:
        flip_function: function that returns True (heads) or False (tails)
        """
        self.heads = 0
        self.tails = 0
        self.flip = flip_function

    def get_score(self):
        if self.flip():  # call stored flip function
            self.heads += 1
        else:
            self.tails += 1
        print('Heads: {}\tTails: {}'.format(self.heads, self.tails))

def do_flip():
    return random.randint(0, 1) == 0  # skip the middle man

obj = TossSimulator(do_flip)
obj.get_score()  # prints the running total heads and tails
# Heads: 0        Tails: 1

A couple of important changes were made. First, I added the __init__ function, which is the constructor for Python objects. This is where you want to define any of your member variables, initialize things, etc. With the class defined this way, you can make multiple instances of your TossSimulator and they will each keep a record of their own scores:

obj1 = TossSimulator(do_flip)
obj2 = TossSimulator(do_flip)
obj1.getScore()  # Heads: 1        Tails: 0
obj1.getScore()  # Heads: 1        Tails: 1
obj2.getScore()  # Heads: 0        Tails: 1  (scored separately)
obj2.getScore()  # Heads: 0        Tails: 2

Second, I made flip_function a parameter that is passed to your constructor. Now you can define a special flip function for each instance of your TossSimulator when you create it, and then get_score will use the flip function defined for that instance every time you call it:

def always_heads():
    return True

obj1 = TossSimulator(do_flip)
obj2 = TossSimulator(always_heads)
obj1.getScore()  # Heads: 0        Tails: 1
obj1.getScore()  # Heads: 1        Tails: 1  (random flips)
obj2.getScore()  # Heads: 1        Tails: 0
obj2.getScore()  # Heads: 2        Tails: 0  (always will be heads)

Third, I cleaned a couple of things up. You can, for instance, replace something like this:

if x == y:
   return True
else:
   return False

with return x == y. They're exactly the same, but one is much more concise (and possibly very slightly more efficient?). You can also directly check the "truthiness" of a function's return, so you do not need to use if self.flip() == True:; you can just use if self.flip():.

You can take that last point a step further and directly check the "truthiness" of a numeric value. In Python, zero is False and all other integers are True. A summary of what values are true and what values are false can be found here.

Upvotes: 1

Related Questions