## Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

### Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today. 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] MOD

Good 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!! I 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
return int(self) + other
return self + other
def __sub__(self, other):
return int(self) - other
def __rsub__(self, other):
return - (self - other)
``` Great 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'>
``` I'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.