Python Object-Oriented Python Dice Roller Giving a Hand

Justyna Julia Jamróz
Justyna Julia Jamróz
13,354 Points

Help with for loop part

Can someone explain to me this part of the code?

        for _ in range(size):
            self.append(die_class())

Why use append? Append to what? And why it's die_class() and not just die_class.

1 Answer

Lets take it step by step.

First we create the class which inherits from the built in list class in Python.

class Hand(list):
    def __init__(self, *args, **kwargs):
        super().__init__()    

At this point aHand() object can be used the same way as a list() object, without it doing anything with the args and kwargs passed to it of course. You can test this out in workspaces for yourself.

Now we want to change our class in such a way that if we pass it Hand(size=2, die_class=Some_Die_Class) it simply creates a list like: [Some_Die_Class(), Some_Die_Class()] containing instances of the `Some_Die_Class ` class provided. We can do this as follows:

class Hand(list):
    def __init__(self, size, die_class, *args, **kwargs):
        super().__init__()

        for _ in range(0, size):
            self.append(die_class())

What we've done in this step is create a for loop which runs size number of times. We don't care for the iteration number so we use '_' when we write our for loop: for _ in range(0, size):.

What we want to do in each iteration is append a new die_class class object to our hand, we can create a die_class object by placing parentheses behind the class name die_class(). Now we simply need to find out how to add this object or instance of die_class to our hand. Luckily we inherited from the list class all its methods, so we can simply use self.append(die_class()) to append the instance we created to self.

Finally for convenience we add default values for size and die_class. To be safe we can also raise a ValueError in case the user hasn't provided a die_class.

class Hand(list):
    def __init__(self, size=2, die_class=None, *args, **kwargs):
        super().__init__()

        if die_class is None:
            raise ValueError("No die_class passed.")

        for _ in range(0, size):
            self.append(die_class())

To get a feel for how it works run it a couple of times, you don't necessarily need to pass it a Die class, you can also pass str or int or whatever as long as it has a class which can be intitialized without arguments or kwargs:

some_hand = Hand(size=10, die_class=str)
some_hand2 = Hand(size=5, die_class=int)

print("output of some_hand: {}\noutput of some_hand2: {}".format(some_hand, some_hand2))

Output:

output of some_hand: ['', '', '', '', '', '', '', '', '', '']
output of some_hand2: [0, 0, 0, 0, 0]
Shreyas Sreenivasa
Shreyas Sreenivasa
7,804 Points

But how is die_class() creating an instance of Die?

Jay Reyes
Jay Reyes
Python Web Development Techdegree Student 15,935 Points

As far as I can see, die_class() may refer to a function or a class (which may be confusing!).

When we import the D6 class at the top of hands.py, it is now recognized in the scope of the file.

When we pass in D6 where die_class() is, it is instantiated.

Why don't we say die_class()=None instead of die_class=None (as seen below):

class Hand(list):
    def __init__(self, size=0, die_class=None, *args, **kwargs): 

I think python wants us to cleanly set parameters. I also think these parameters are very abstract, but it's our intention beforehand to make them concrete inside the definition of the methods in the code.