Harris Handoko3,387 Points
Help needed on frustration.py
Kenneth is a brilliant guy, but I really have been lost following his lectures on OOP. But I really want to get to the bottom of this: I know people have been posting the answer on the forums, which was really the only way I managed to pass this challenge task.
import random class Liar(list): def __init__(self,*arg): self.arg =  def __len__(self): super().__len__() randomNum = random.randint(0,10) x = randomNum + len(self.arg) return x
A couple of them seems to have overriden init again in their Liar(list) class.
Why is it necessary to define init again? Or is it the conventional way? If I just removed the init and moved that self.arg =  into the len method, it works just the same.
import random class Liar(list): def __len__(self, *arg): self.arg =  super().__len__() randomNum = random.randint(0,10) x = randomNum + len(self.arg) return x
Why is it that if I don't use random library, the checker won't let me pass? I thought as long as it spits out anything other than the actual value, it should be fine. I tried removing those import random, randomNum = random.randint(0,10) lines and changed the randomNum to some value: x = 100 + len(self.arg)... and it didn't work.
Louise St. GermainTreehouse Moderator 16,916 Points
As you suspected, a lot of the code above is not necessary. I was able to pass the challenge with only 3 simple lines of code, as follows:
class Liar(list): def __len__(self): return super().__len__() + 2
Line by line, this is what it's doing:
- Create the Liar class as an extension of the list class.
- Define a method called _ _ len _ _ , which defines what it will do when someone calls the method on an instance of the Liar class. e.g., liar_list_length = len(my_liar_instance).
- When someone does call my Liar class len() method, tell Python to just use the regular len function that's already defined for lists (super() refers to the parent class, in this case, list; and _ _ len _ _() refers to the list class's pre-defined len() function). Then add 2 to whatever comes from that, to change it from the true length to a fake length.
A few quick comments:
- You don't need a random number. You could use one, as in the example you posted, but it's an extra complexity that is not necessary here. Also, you wouldn't want 0 as an option (as above), since if you add 0 to the real length, you still have the real length, and not a fake length! My code just arbitrarily adds 2 to whatever the actual length is, and it was fine.
- You also don't need a special _ _ init _ _ method. You would put code here if you wanted it to do something special that doesn't happen with a regular list, but since it's supposed to just create a list like usual (and this is a quick coding challenge!), I'm just letting it defer to the parent _ _ init _ _ method.
- The *arg is not a necessary addition to the parameters of the _ _ len _ _ method. If you include it, it's because you want to collect any extra values that the user might pass when they call the len() method on a Liar object. For example, if they call len(my_liar_instance, 12, "apple", "photo_of_grandma.jpg") instead of just len(my_liar_instance). But since you have no use for any extra arguments here, you don't need to worry about any of that inside the method. The list that we want the length of is part of self, and is not within those extra args. I chose to leave *args out entirely since there's no good reason that anyone would call len() with extra arguments.
- Also, in the original code, self.arg is redundant. It's initialized to an empty list, and it's never updated anywhere. So there's no point in having it there anyway, because its length is always 0. Python won't make any automatic connection between that and the *arg in the parameter list.
- Meanwhile, in the original code, super()._ _ len _ _() is called, which is what you want (that's what gives you the length of your list). However, the result of it is not captured anywhere, so the result just disappears into a black hole.
I hope this helps a bit!
Louise St. GermainTreehouse Moderator 16,916 Points
Yes, the list in general already has a _ _ len _ _ defined - it's not specific to this context. Let's say you do this (non-OOP) type of thing:
my_list = [1, 2, 3] # This creates a list. length = len(my_list) # This will return 3
When you created the list in the first line, in the background, Python called the list class's _ _ init _ _ method. This was "behind the scenes" as far as you're concerned as a user.
Similarly, when you called len(my_list), what Python really did was translate that into a call to the list class's _ _ len _ _ method that's built into that class, and the argument my_list that you passed to it is what turns into self when you're looking at it from inside the function. It's a reference/pointer to an instance of a list-class object so that the function knows which list you're talking about.
In general, those "dunder" functions represent some sort of pre-defined operation you can do on the built-in classes, and they are usually represented with simple operators on the user side. Python then translates these into the dunder methods behind the scenes. You only need to worry about them in OOP because then, you are going "behind the scenes" yourself to build your own classes.
sum = 1 + 2 # calls the __add__ method for int class (normal addition) counter += 1 # calls the __iadd__ method for int class (in-place addition) "apple" == "orange" # calls the __eq__ method for str class (checking for equality)