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 trialEwerton Luna
Full Stack JavaScript Techdegree Graduate 24,031 PointsWhy do I have to use self = str.__new__(*args, **kwargs)? I tried not using it and it worked.
class Reversed(str):
def __new__(*args, **kwargs):
self = str.__new__(*args, **kwargs)
self = self[::-1]
return self
So Kenneth called the str new method passing whatever arguments we give it. I read online that when overriding a new method you will always have to call the super class new, and that's kinda of what Kenneth does there.
Just out of curiosity, I tried not calling the super class new with a different version of the code:
class Reversed(str):
def __new__(cls, value):
self = value
self = value[::-1]
return self
And it worked fine.
>>> name = Reversed("whatever")
# returns 'revetahw'
The same goes to this code from Kenneth's example:
class FilledList(list):
def __init__(self, count, value, *args, **kwargs):
super().__init__() # This creates an empty list, but is this line necessary?
for _ in range(count):
self.append(copy.copy(value))
I tried the SAME code but only without using super().init() to create an empty list, and it worked fine.
So why do I have to call the super class init and new in these cases even if without them the code seems to do the same?
I keep researching online and this new versus init thing isn't still very clear to me. I know I still have a lot to learn, but it would be nice to know why do I have to write the code in a certain way.
2 Answers
Chris Freeman
Treehouse Moderator 68,441 PointsAgain, another great question! The advantage of using the super
or str.__new__
is be able to use the automatic type converters built into the str.__new__
method and gain access to the slicing methods available to the str
class.
Try using an integer such as Reversed(12345)
on both of the versions above. Without the call to super
you would be effectively trying to take a slice of an integer causing a TypeError: 'int' object is not subscriptable
.
Edit: Adding a bit more detail. In every object creation, both the __new__
and the __init__
methods are run. The non-obvious behavior is the __new__
calls __init__
. This is why __init__
methods do not include a return
statement. The newly created and initialized object is returned by the __new__
method. Think of the __init__
method as merely sprucing up the newly create object with attribute values, etc.
Post back if you have more questions. Good luck!!
Ewerton Luna
Full Stack JavaScript Techdegree Graduate 24,031 PointsFirst of all, Thank you so much, @Chris! You are one of the reasons why learning from TeamTreehouse is so worth it. :D
I think I understand everything you said, I don't have any questions about it. The only problem is that I think I might not be connecting the dots because I still can't understand why those errors were happening.
Okay, new is a static method (I didn't know that), so it makes perfect sense not to include either "cls" or "self" in it. I only included it because Pycharm's autocomplete did it for me. So just for the heck of it, I experimented doing what I wrote on my first question.
What is really happening is more obvious if I rewrite cls, *args, **kwargs as args[0], *args[1:], **kwargs. Here you can see that cls is really what would otherwise have been the first of the args list.
From your answer, it seems to me that including "cls" in the new definition will make "cls" receive the first argument when ReversedStr is called. Any other argument besides the first one will be packed into args. In conclusion, cls would be like any other regular positional parameter. Understanding that, I think the code I wrote above should work, but it doesn't.
class ReversedStr(str):
def __new__(cls, *args, **kwargs):
self = str.__new__(*args, **kwargs)
return self
# Python Console:
# >>> name = ReversedStr("Ewerton")
# returns: TypeError
I'll try to explain what I think should happen line by line when I run name = ReversedStr("Ewerton") on the console:
- "Ewerton" will be passed to the cls argument. *args and **kwargs won't receive any argument.
- cls is not being passed into str.new, but args and kwargs are. In this case, since args and **kwargs haven't received any arguments, str.new should return an empty string. Therefore, *self is assigned to an empty string.
- Since self value is an empty string, the new method will return an empty string.
In the case where I include "cls" both in the definition and inside the str.new call:
class ReversedStr(str):
def __new__(cls, *args, **kwargs):
self = str.__new__(cls, *args, **kwargs)
self = self[::-1]
return self
# Python Console:
# >>> name = ReversedStr("Ewerton")
# returns: notrewE
- "Ewerton" will be passed to the cls argument. *args and **kwargs won't receive any argument.
- cls WILL be passed into str.new, just like args and kwargs are. In this case, str.new will receive "Ewerton" as the first argument. Since args and kwargs haven't received any argument, str.new will receive only "Ewerton".
- str.new("Ewerton") would return "Ewerton", therefore self is assigned to "Ewerton".
- The new method will return "notrewE"
I know I'm wrong on the first case, otherwise I wouldn't get an error. And I know I might be wrong on this second case too, because even though it works, It might not be from the reasons I think it works.
Chris Freeman
Treehouse Moderator 68,441 PointsIโve added a comment to my previous answer to address the TypeError
as seen in your first example. Your second example and explanation is correct.
Of course, none of the examples will actually reverse the string without the slice reversal statement, left out for brevity. ๐
Ewerton Luna
Full Stack JavaScript Techdegree Graduate 24,031 PointsEwerton Luna
Full Stack JavaScript Techdegree Graduate 24,031 PointsIt makes total sense to me. Thanks so much for helping! I've learned a lot from you!
Ewerton Luna
Full Stack JavaScript Techdegree Graduate 24,031 PointsEwerton Luna
Full Stack JavaScript Techdegree Graduate 24,031 PointsHey, Chris! Could you answer this follow up question for me please?
I was playing around with this ReversedStr class. When I typed def
__new__
(, PyCharm autocompleted including a cls parameter. So far so good.So I tried to run the same code, only including the cls parameter like this:
So when I went to the console and ran the following code, resulting in a TypeError:
I kept wondering why was this happening. Then, to try to fix it, I included a cls parameter into the
str.__new__
Now, the code ran perfectly.
So if I include the cls parameter inside def
__new__
, I also have to include insidestr.__new__
. The same logic goes if I DON'T include the cls parameter inside the def__new__
: in this case, I have to NOT include cls insidestr.__new__
Can you tell me the reason why this happens? I understood the logic of using cls in other cases, but I can't figure out on this one!
Thank you in advance!
[MOD: added ` escapes around dunder names -cf]
Chris Freeman
Treehouse Moderator 68,441 PointsChris Freeman
Treehouse Moderator 68,441 PointsGreat follow-up question!: So if I include the
cls
parameter insidedef __new__
, I also have to include insidestr.__new__
. The same logic goes if I DON'T include thecls
parameter inside thedef __new__:
in this case, I have to NOT includecls
insidestr.__new__
? Yes, but not for the reasons you may be thinking. The short answer iscls
should never be included in the__new__
method definition because__new__
is a static method not a class method! That is, the parameter lists should be*args, **kwargs
, ormyarg, *args, **kwargs
if your__new__
method needs to use a parameter that will not be passed along in thesuper()
call.The reason using
*args, **kwargs
in both places orcls, *args, **kwargs
in both places works, is that they are virtually the same. Since__new__
is a static method it does not consume the first parameter to refer to the class (cls
) or a class instance (self
). Since no class object exists yet (nothing new as been created to refer to), there is no meaning to this parameter as there are no other methods or attributes to be referenced. What is really happening is more obvious if I rewritecls, *args, **kwargs
asargs[0], *args[1:], **kwargs
. Here you can see thatcls
is really what would otherwise have been the first of theargs
list.Update: Video Correction: In the video subclassing builtins, Kenneth says: "
The __new__ method does not use "self" because it is a class method."
This is inaccurate.
classmethods
typically usecls
as the first parameter. Since__new__
does not usecls
(orself
) it is a static method.Refresher:
self
to refer to the class instance and it's methods. May be referenced byinstance.method()
orClass().method()
(note use of first set of parens).cls
to refer to the defined class. May be referenced byClass.method()
(note only method as parens). The class method should be decorated with@classmethod
self
orcls
and does not consume first parameter to reference the instance or the class. May be referred to usinginstance.method()
orClass.method()
. Excluding__new__
, the static method should be decorated with@staticmethod
Again, post back if you have more questions! Good Luck!!
Chris Freeman
Treehouse Moderator 68,441 PointsChris Freeman
Treehouse Moderator 68,441 PointsI forgot to address the error youโre seeing :
TypeError: str.__new__(X): X is not a type object (str)
Looking at the code below:
In the case where
cls
is included in the method parameter list but not included in thesuper()
call, theTypeError
is caused by the โemptyโ*args
be equivalent to an empty list. The error you get can be seen if an empty list is passed to `super:If an empty
*args
were equivalent toNone
, the error would have been:TypeError: str.__new__(X): X is not a type object (NoneType)