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 trialPhilip Schultz
11,437 PointsWhen to use self.value when overloading
Can someone explain to me what is happening in this video? Why is Kenneth using 'self.value' in some cases and 'self' in others. How are we suppose to know when to use one or the other?
For example, here is a piece of code from the video on using __add __
.
def __add__(self, other):
if '.' in self.value
return float(self) + other
return int(self) + other
The magic methods above the __add__
methods all use self.value.
For example, here is the int
method.
def __int__(self):
return int(self.value)
How can you use int(self) if the instance is more complex? I'm assuming this is a simple example in the video and the class only has one attribute, but what if there are multiple attributes. Wouldn't you have to use self.value or self.paycheck or what attribute you wan't to do the math with?
Also, can someone explain how __radd__
is working? I thought the purpose of making it is because we are not able to add an instance of NumString to a number if it's located on the right of the equation. If that is the case why are we typing self + other and not other + self, with the instance on the right side. Is it implicitly calling __add__
somehow? Why don't we have to check the value is a float or an int like in __add__
?
[MOD: added ` escapes on method names -cf]
3 Answers
Chris Freeman
Treehouse Moderator 68,441 PointsGood questions Philip!
In the following explanation keep in mind that self.value
refers to the stored string value and self
refers to the NumString
object. The key to knowing which to use is to think about the context. Is the string value needed or the object itself needed? The answer is sometimes it doesn't matter.
In the __add__
method, addition usually returns a number, so the __add__
method needs to convert the stored string value to a number before adding to the other operand passed in. The simplest way would be to use the methods from this class since they have been written explicitly for this purpose. That is, the __int__
and __float__
methods return the appropriate numeric representation of the NumString instance for use in addition. Calling int(self)
or float(self)
will explicitly call the __int__
and __float__
method of the "self" object. Since both the __int__
and __float__
methods operate on a NumString object, an object must be passed to it.That is why self
is used. The condition in the if
statement is checking for a decimal point in the value, so it must use self.value
to see the actual object value.
In the __int__
method, the object is to get back a numeric integer value. self.value
is used to as the argument to int()
so that the objects value can be converted from a string to an integer. If return int(self)
had been used, it would trigger another call to __int__
causing an infinite loop. This recursive call to __int__
would end in an "RecursionError: maximum recursion depth exceeded while calling a Python object
" Yikes!
In the NumString
class, the __add__
method could have used int(self.value)
instead of using int(self)
since int(self)
calls __int__(self)
which returns int(self.value)
anyway.
The main point of using the object reference in int(self)
is that it is not always obvious which attribute should be used in the context of addition or exactly how a numeric value for an object should be created. Using int(self)
will always call the __int__
method of the class which is explicitly written to provide the numeric context value. If the __add__
method converted the object to a numeric context in its own way it could possible differ from the process used in __int__
and would most certainly violate the DRY principle.
In the __radd__
method, by using self + other
it triggers a call to __add__
with the same arguments since the "self" is now on the left side of the operator instead of the right side. In most cases, object addition is symmetrical (a + b == b + a), so it is effectively passing the task along to __add__
method. If other + self
had been used in __radd__
, an infinite loop would be created as other + self
triggers another call to __radd__
. Yikes, again!
Since __radd__
redirects the call to __add__
, there is not need to check the if the value is a float
or an int
since it is covered in the __add__
method.
I hope I covered it all. Post back if you have more questions! Good luck!!
Iulia Maria Lungu
16,935 PointsI would like to add something to the great explanation of @Chris Freeman already gave because it made me understand things better. Regarding this:
Also, can someone explain how
__radd__
is working? I thought the purpose of making it is because we are not able to add an instance of NumString to a number if it's located on the right of the equation. If that is the case why are we typing self + other and not other + self, with the instance on the right side. Is it implicitly calling__add__
somehow?
This would not work if the implemented methods were not __add__
and __radd__
but __sub__
and __rsub__
, just because addition or multiplication are commutative mathematical operations but subtraction for instance is not. Below is what I did to have subtraction in place.
class NumString:
def __init__(self, value):
self.value = str(value)
def __int__(self):
return int(self.value)
def __str__(self):
return self.value
def __add__(self, other):
return int(self) + other
def __radd__(self, other):
return self + other
def __sub__(self, other):
return int(self) - other
def __rsub__(self, other):
return - (self - other)
Chris Freeman
Treehouse Moderator 68,441 PointsGreat points! If needed you can also define __neg__
to be called when you have unary negation as in your last statement if needed.
return - (result)
But since (self - other)
above returns an int
, the int.__neg__
would be run instead of NumString.__neg__
.
Also be careful with Kenneth’s implementation of __iadd__
. As written, it changes the object from NumSting
to an int
.
In the https://teamtreehouse.com/library/math video, the NumString.__iadd__
method uses self.value = self + other
. Since self + other
calls NumString.__add__
, and it produces an integer result, this changes self.value
from a stored string value to an integer value. So this instance's __iadd__
would only be called the first time. Also, it overwrites the class instance creating an integer in its place. Using self.value = str(self + other)
would be more correct. Adding print statements to all of the scripted NumString
methods yeilds:
>>> a = NumString(5)
__init__ run with value: 5
>>> type(a)
<class 'NumString.NumString'>
>>> a += 1
NumString5 running __iadd__ with other value: 1
NumString5 running __add__ with other value: 1
NumString5 running __int__
>>> a
6
>>> type(a)
<class 'int'>
Anthony Crespo
Python Web Development Techdegree Student 12,973 PointsI'm no expert but maybe I can help.
self represents the instance of the class. By using the "self" keyword we can access the attributes and methods of the class in python.
Outside of a class you call an object by it's name.
Var_one + Var_two
Var_one.function()
But when you are writing a function inside your class the name of your variable is lost. That's why we call "self" and not the "Variable_name". So when you create the __ int__ function you write:
def __int__(self):
return int(self.value) # self.value equal to variable_name.value if it was outside "class"
When you create that function above inside your class, your class now have the possibility of been represented in an integer value. Without that function it doesn't know what to return if you want to turn your object in to an integer. It's the same thing for an object of type int. Inside that int class there is some functions that does the same to represent that int value in other variable type. But if you create a new type of variable like "Character" and want to convert it in to an int like this:
>>> variable = Character() # variable type is Character
>>> int(variable) # You pass a Character type object to an int
# You get this message error cause it doesn't know how to represent a Character in to an int.
TypeError: int() argument must be a string, a bytes-like object or a number, not 'Character'
TLDR : Inside your __ int__ function you return int(self.value) that represent a str type, a type int already know how to represent and inside your __ add__ function you return int(self) cause now it's possible to represent your object in to an int cause of that other function.