Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Object-Oriented Python Dice Roller RPG Roller

Johannes Scribante
Johannes Scribante
19,175 Points

Part 2 of code challenge, 1. cannot get length of hand and 2. passing a class as argument on the class's method

I have implemented this code locally and here are the scenario's I have tested.

h20 = Hand(size=5)  
print(h20)  
# output: [17, 10, 17, 9, 2]  

print(h20.total)  
# output: 55  

h20.roll(4)   
print(h20)  
# output: [8, 17, 20, 2]  

print(h20.total)  
# output: 47  

print(len(h20))  
# output: 4  

h2 = Hand.roll(Hand(), size=3)  
print(h2)  
# output: [17, 12, 10]  

I have two questions:

  1. why does it say "cannot get length of hand" when I can print the length? Maybe not the best question, but I am struggling to find what to debug

  2. when I put the input of h2 = Hand.roll(Hand(), size=3), why would I need to specify the class or "self" in the method when I am calling the method on Hand.roll?

Some guidance and insight would be much appreciated :)

dice.py
import random


class Die:
    def __init__(self, sides=2):
        if sides < 2:
            raise ValueError("Can't have fewer than two sides")
        self.sides = sides
        self.value = random.randint(1, sides)

    def __int__(self):
        return self.value

    def __add__(self, other):
        return int(self) + other

    def __radd__(self, other):
        return self + other

    def __repr__(self):
        return str(self.value)

class D20(Die):
    def __init__(self):
        super().__init__(sides=20)
hands.py
class Hand(list):
    def __init__(self, size=2):
        super().__init__()
        self.size = size
        for _ in range(size):
            self.append(D20())

    @property
    def total(self):
        return sum(self)

    def roll(self, size):
        for item in range(size):
            if item+1 > self.size:
                self.append(D20())
            if item+1 == size and self.size > size:
                del self[item]
            if item+1 < self.size:
                self[item] = D20()
        self.size = size

1 Answer

Chris Howell
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Chris Howell
Python Web Development Techdegree Graduate 49,702 Points

Hello Johannes Scribante

So I copied your classes, I removed parts of the class to shorten a little so I could add comments into it to explain some of whats going on. First lets start off with understanding the comments. Then if you are still confused we will go from there.

hand.py

class Hand(list):
    """
    Answer 1
    Since Hand inherits from list python data type
    You are given some prebuilt things for free.

    One of these things is a method called __len__().

    __len__() defines how a list will print the length of itself.
    Because not all Objects know how it should count itself or the
    data it may contain.
    """

    # ... rest of class removed 

    def roll(self, size):
        """
        Answer 2

        self is normally passed to methods
        inside a class when you need to DO something
        with 1 specific instance of that class.

        This means the instance HAS to be created before
        you can call this method.

        Other types like classmethods or staticmethods
        do not require self to be passed. And do
        not require an instance to be created.
        """
        for item in range(size):
            if item+1 > self.size:
                self.append(D20())
            if item+1 == size and self.size > size:
                del self[item]
            if item+1 < self.size:
                self[item] = D20()
        self.size = size

dice.py

class Die:
    # ... rest of class removed.
    def __repr__(self):
        """
        Without this method, print any instance
        of a die or any instance that inherits from
        this Die class would print from the
        default Object __repr__ method.


        Because all classes in Python 3, by default,
        inherit from the object data type.
        """
        return str(self.value)


class D20(Die):
    """
    Just like Hand inherits all methods from list.

    D20 is inheriting all the methods you defined from Die.
    With the exception of the __init__ method, because
    you have overridden the default inherited one.
    """
    def __init__(self):
        super().__init__(sides=20)
Chris Howell
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Chris Howell
Python Web Development Techdegree Graduate 49,702 Points

In your output code, I seen this line.

h2 = Hand.roll(Hand(), size=3)
    print("h2: ", h2)
    # output: [17, 12, 10]

I copied your code into a Workspace and ran it. I definitely didnt get an output of [17, 12, 10] But I shouldnt have, I dont know if this was what you were expecting to come back? or an output you accidentally left.

so to explain what is happening in those lines.

# 1. You are telling Python to call the roll method on Hand.
# 2. Then you are telling it to store the returned value from roll into h2 variable.

h2 = Hand.roll(Hand(), size=3)

# Problem: roll needs a self argument (which has to be an instance). 
# In first argument, you created a new instance and passed it as that argument.
# But since roll doesnt return anything... you just lost that instance of Hand.
# and h2 will become None.
# You could have roll method return THAT copy of self.
# then h2 would hold that new instance and you wouldnt lose it.

# I probably wouldnt recommend doing this, you could make a classmethod instead.
# But for learning purposes I am telling you how to MAKE your code work.
# So you can play with it some more. :)

Let me know if that makes sense or if you have more questions!

Johannes Scribante
Johannes Scribante
19,175 Points

Thanks Chris for the help! The answers you gave weren't quite what I was hoping, usually when we get stuck with a problem we normally just want it solved without too much understanding as long as it works. You however nudged me in the right direction to solve the problem.

Thanks for all the comments! Really helped again just to understand some of the smaller issues like the __repr__ and what self means in the instances and classes.

One thing I missed was to include the import of the D20() class.

Also here is the sudo code I used to finally solve the code challenge. Hopefully it helps and explains correctly to someone else. I would recommend going back to the video about books and the bookshelf (https://teamtreehouse.com/library/constructicons) this really helped

class Hand(list):
    # set __init__ of Hand()
        # create new list instance by giving List(dice_in_hand) else default to None
        # therefore dice_in_hand should be a list

    @property
    def total(self):
        return sum(self)

    @classmethod    # call a method on a class, without the class instance yet existing
    # pass it a class (itseld Hand) and an argument size_of_hand we want
        # initialize an blank list of dice_in_hand
        # loop through the size_of_hand we want
        # append new instance of D20() to dice_in_hand
        # return the newly created instance, when you call the cls, to create a new instance of the class passing it the list of dice_in_hand. I.e. call the __init__ of Hand()

If anyone has some tips on how to comment better, please let me know :)

These code challenges are really helpful to put into practice what we learn!

Chris Howell
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Chris Howell
Python Web Development Techdegree Graduate 49,702 Points

Well I promise that I will never just GIVE the answer. :-)

In programming every problem can have multiple solutions, of those solutions there is generally 1 really good solution for that specific scenario. So just handing out answers doesn't help at all. But if you can teach the concepts and show the person how to figure it out, then they will be better prepared for the next problem they come across to be able to solve it.

I avoid giving answers at all time. Its actually a little irritating when I see other students just copy/pasta their solution code without trying to help them learn first.

Johannes Scribante
Johannes Scribante
19,175 Points

Hi Chris, sorry if this feels like spam, I'd rather ask than not. Regarding my comment where I included my code as sudo code, would you say even that is to clear cut and would take away the opportunity from someone else to learn?

I'd be more than happy to remove my code snippet. I thought it may guide someone else in a direction without giving them the answer, I also don't way to push them into thinking that is the way to solve the problem. Like you said there may be other ways to do it and this happened to be mine. I don't know if it's the best but it worked.

Part of the learning process is to struggle until you hopefully understand why you are doing what you do.

Chris Howell
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Chris Howell
Python Web Development Techdegree Graduate 49,702 Points

As long as you arent giving the direct copy/pasted code should be fine. Pseudo code is generally just your thought process on writing the code. The students still have to derive the coded answer to pass the challenge. So I would say your pseudo code comments are fine.