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 Advanced Objects Frustration

I'm completely stuck on this question and can't figure it out at all, can someone help?

.

9 Answers

Jennifer Nordell
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

Hi, Robert Peters! Don't give up :smiley: First, I'm sure you know what a list is by now. This class is inheriting from the built-in class of list. Whenever you create a list or really a string or any other thing, it gets a whole slew of things attached to it. For instance, a string gets a .upper() and .lower() method. Yes, it is a data type, but it is wrapped inside an object which provides extra functionality. Similarly the len() function will tell you how long an iterable is. So any built-in type that you can use len() on has a __len__ defined on the object. Even lists. It's a little belated, but I made a Halloween-themed example for you:

candy_bucket = ["snickers", "reeses", "skittles"]  # just a regular list with some candy

print(len(candy_bucket))  # this prints out 3

class Liar(list):
    def __len__(self):
        return super().__len__() + 3  # we don't need self passed in and it would cause an error because __len__ takes 0 arguments


fake_bucket = Liar(candy_bucket)  # a fake candy bucket 
print(len(fake_bucket))  # prints 6

When you make a list you are calling an __init__ method for the list object. Then it gets a __len__. You only need to run the __len__ of the list object (or super) and then modify the results.

Hope this helps! :sparkles:

Thanks for the answer! I really don't get it though. How does it know what to operate on if nothing is sent to it? Without self or a variable containing a list passed to it, how does it know what to tell you the length of?

Jennifer Nordell
seal-mask
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

Hi, again Robert Peters! We did pass in something, though :smiley: We passed in the candy_bucket which is a list. Now you can't actually see the class for list here, but it also has an __init__. It's setting internal attributes at the time we create a list. The candy_bucket is being passed into the super class. That's how it knows. It's running the __len__ on that list we passed in.

If we had done fake_bucket = Liar() with no list available it would print out 3. Because the super class of list had nothing passed to it so it has a len() of 0. So printing the len() of a Liar() would print 3.

But it's not:

super().__len__(candy_bucket)

It hasn't been passed anything, I don't understand sorry. When you define a method the first bit is always self (that's what's taught in the videos), but (dunder)len(dunder) takes no argument?

In Stivan's example he shows:

def __len__(self):
        return len(self._list)

But no self is passed to it later on, even though it's literally right there as the fist argument required.

How did you pass candy_bucket to it? I can't see it being passed in your code, just

super().__len__() + 3
Jennifer Nordell
seal-mask
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

Hi, Robert Peters! I passed it when I did Liar(candy_bucket). I think the part that's eluding you is the fact that a list is a class. I cannot show you the internal code for that class because it is built-in to Python. It's not something you nor I wrote. The Liar class is inheriting from the list class. When you make a new list it has an __init__ that it runs and it has methods like .append() etc. Your Liar class is inheriting all those. The only thing it needs to override is the __len__ method so that it returns something that is inaccurate. I've put together an expanded example that combines different ways to create a list. When you create a list it's running class methods like __init__ inside the list class :smiley:

candy_bucket = ["snickers", "reeses", "almond joy"]  # normal list with three types of candy bars

print(candy_bucket.__len__())  # printing the length of the candy bucket

halloween = list("halloween")  # creating a list from an iterable in this case, a string

print(halloween)  # prints ['h', 'a', 'l', 'l', 'o', 'w', 'e', 'e', 'n']
print(halloween.__len__())  # prints the length of the halloween string ... 9


class Liar(list):
    def __len__(self):
        return super().__len__() + 3


fake_bucket = Liar(candy_bucket) # creating a new instance of Liar based on candy_bucket
fake_string = Liar(halloween)  #creating a new instance of Liar based on halloween

print(len(fake_bucket))  # print the length of the fake_bucket 6
print(len(fake_string))  # print the length of the fake_string 12


# The append method would not be available to Liar if it did not inherit from the list class
fake_bucket.append("skittles") # appending skittles to the fake_bucket 
fake_string.append("!")  # appending an exclamation point to fake_string

print(fake_bucket) # prints ['snickers', 'reeses', 'almond joy', 'skittles']
print(fake_bucket.__len__()) # prints 7
print(fake_string) # prints ['h', 'a', 'l', 'l', 'o', 'w', 'e', 'e', 'n', '!']
print(fake_string.__len__()) # prints 13

Hoping this helps! :sparkles:

edited for additional information

At the risk of complicating this, I feel obligated to point out that every class in Python gets an __init__ even if you don't define one. In the case that you haven't provided an __init__ for a subclass, then the __init__ of the super class is called. So when I do Liar(candy_bucket) the init of Liar is called, which is empty so it calls the __init__ of list and sends it the list.

As far as __len__() not taking any arguments, it's the the __len__ of the super class (list) that does not take any arguments besides self. The list class is providing self because self is different in the super class. It's an instance of list. Not an instance of Liar. So if you were to try and call super().__len__(self) you would be sending in an instance of Liar to the list class. And it is already defined as taking itself, an instance of a list. It would be getting one too many things. An instance of list and an instance of Liar.

Thanks! I think I'm getting it...

The "So when I do Liar(candy_bucket) the init of Liar is called, which is empty so it calls the init of list and sends it the list." part definitely helped me get some of the previous vids.

Just quickly, does this mean that super() automatically sends self? And if so, does it always send self? The last paragraph about "The list class is providing self because self is different in the super class. It's an instance of list. Not an instance of Liar." is the bit that's getting me I think.

Thanks for the help!

Jennifer Nordell
seal-mask
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

Robert Peters You're getting it! The list has methods like __len__ and __init__ itself. So yes, a list has those methods that take self where self is the instance of the list. But, again, on Liar, if you send in self to the super().__len__() it's only expecting self where self is the instance of list... not Liar :smiley:

The __len__() of the super class already takes self :smiley: The actual code for a list class is actually implemented in the C language so I can't really reasonably show you that. But imagine, if you will that class list() has a def __len__(self): :smiley:

It only accepts itself. Not any subclass's self :tada:

# Now I want you to make a subclass of list. Name it Liar.
class Liar(list):
    # Override the __len__ method
    def __len__(self):
        # return the wrong number of items in the list
        return super().__len__() + 3

Thanks! can you explain why no 'self' is sent to the

super().__len__(self)?
# It's been a while since I've used OOP but I know that in this case
super().__len__() # __len__() does not accept any arguments

Isn't it just working out the length of nothing then?

# Not really
# Take a look at the list class that I created.
# I believe that it might be similar to what the list class looks like for the challenge
class list():
    def __init__(self, _list = ["test", "test1", "test2"]):
        self._list = _list

    def __len__(self):
        return len(self._list)

# When we call the
super().__len__()
# It calls the __len__ method of list class and returns the
return len(self._list) # 3


class Liar(list):
    def __len__(self):
        # so now, super().__len__() would return 3, and 3 + 3 = 6
        return super().__len__() + 3

You practice by creating short and fun programs. Check out this link, https://www.upgrad.com/blog/python-projects-ideas-topics-beginners/

Awesome, cheers! How far do I need to go before I can start doing some of those? Is it years, or if I keep going and actually manage to understand this python track within the next few months, can I do them then?

I'm sorry, it's probably a great answer, I just don't think I'm smart enough to be learning to code anymore. I've gone through these object-oriented python videos over and over again and I just seem too dumb

Vincent Zamora
Vincent Zamora
3,872 Points

I feel the same way. Some of the stuff goes right over my head (really). What helps me sometimes is to do a video google search on the subject and walk through them.

I do like Jennifer's example.

Don't say that. You just need to keep trying and practice some more. Object-Oriented Programming is probably one the hardest thing for new programmers. You just have keep going. If you don't understand some concept just google it or ask us here in the community. We can help you go through this. Most of the times I experience the same thing. I'm trying to code in multiple languages and it's hard sometimes to remember syntax and concepts but I never give up. If I don't remember or understand something I google it. Without google I probably won't be able to code because my head is a mess. The way I learn is by look at other people's code and asking myself why do they use this for that and that for this.. etc. Trust me, I'm at the same position you are in. You just need to keep practicing and don't give up.

Cheers man, I'll give it shot. I guess I don't know what I'd be googling, maybe "how does len know what to operate on without self?" Or "why does def len(self) have the argument self if it doesn't use it and you don't have to add it later?" Where did it go?

How do you practice by the way?

You can start now and try to build it as much as you can. As you learn new things, you can add to your code and improve your old code.

Johnathan Martinez
Johnathan Martinez
30 Points

I agree with Stivan! Projects are definitely the way to go. You can also check out this link: https://www.dataquest.io/blog/python-projects-for-beginners/. It discusses why projects are the best way to learn. It's an extremely helpful framework for people just starting out. Good luck!

Ruby Wu
Ruby Wu
7,989 Points

Thank for Jennifer Nordell providing the great example. It really helps me to understand the concept of this challenge. And also thank for Robert Peters to point out the question I also struggle with. I appreciate it! :tada: