Serdar Halac15,259 Points
Why does this code re-set the self.value attribute from a string to an int?
So I answered the question fairly easily, but I still don't understand why the code was coded as such:
def iadd(self, other): self.value = self + other return self.value
when in init : def init(self, value): self.value = str(value)
It's clear that init sets the value attribute to a STRING (since this whole class is supposed to be a string representation of a number). But the iadd method calls the add method, which returns an int (return int(self) + other) or float, so that means that in turn the iadd method sets self.value, which as as I just pointed out was initialized to a STRING, to the result of the add function which is a FLOAT/INT. Doesn't that completely change the class' purpose entirely?
It gets weirder because I ran some of this code on sublime text and the Mac terminal and did:
numTest = NumString(8)
Which makes sense. Right? the value attribute is a class string, and the instance itself is an instance object of the NumString class.
But then continuing the code above and doing:
numTest += 7
Traceback (most recent call last):
File "treetest5.py", line 44, in <module>
AttributeError: 'int' object has no attribute 'value'
So ok, it sets numTest to numTest + 7 which is equal to 15, so thats what the new value attribute is. But then asking for the type of the value attribute, it no longer says class string! It now just says int object has no attribute value!
So it seems that the code provided by TreeHouse DOES change the class' purpose entirely, as far as I know (changing the value from a string representation of an int to a straight up int), but I don't know why. I also have no idea why print(type(numTest.value)) didn't just return <class 'int'>, and I really want to know!
So please help out, because I can pass the assignment and did, but have no clue why that's the way the Iadd and imul methods are supposed to be done. As far as I know they break the purpose of the class itself and such code would only be suitable for a class where the value itself is not supposed to be a string but an int.
Programming wisdom would be greatly appreciated.
class NumString: def __init__(self, value): self.value = str(value) def __str__(self): return self.value def __int__(self): return int(self.value) def __float__(self): return float(self.value) def __add__(self, other): if '.' in self.value: return float(self) + other return int(self) + other def __radd__(self, other): return self + other def __iadd__(self, other): self.value = self + other return self.value def __mul__(self, other): if '.' in self.value: return float(self) * other return int(self) * other def __rmul__(self, other): return self * other def __imul__(self, other): self.value = self*other return self.value
Chris HowellTreehouse Staff
Hi Serdar Halac
So I do see where you are confused here. It does look like there is in-fact a side effect with the code in this section. I think the overall take away Kenneth wanted you to have was how to use the the magic methods and what they went to.
But if you are curious of how to improve upon this class the problems are with the return of each of the magic method methods.
A few examples, we will only look at addition:
# Lets make NumString of 8. ns1 = NumString(8)
Now we know, anytime we do ANYTHING with this class. At the end of it, we would like to keep our NumString class type. We dont want to lose it to another class type like int or str.
So lets start out with our print of ns1 from above.
# Print the type of ns1 print(type(ns1))
We should get...
>>> <class 'numstring.NumString'>
Because we haven't done anything except create the class.
Now we will perform some math and check our type again.
# Now lets do some math which should give us the issue. ns1 += 8 # And re-print print(type(ns1))
We should get...
>>> <class 'int'>
This seems confusing because its not what we expected back. But it is in-fact what we told the code to do if you look a bit closer.
When we called the
+= we really called the
__iadd__ method on our class.
So lets go look at what is happening.
class NumString: # ... other methods ... def __init__(self, value): self.value = str(value) def __iadd__(self, other): self.value = self + other return self.value
When we say
ns1 += 8, it is shorthand for:
ns1 = ns1 + 8.
We are setting the variable
ns1 to whatever is returned by the right side of the equals sign. Inside the
__iadd__ method we can see that it is returning
self.value which has become an int type before we returned it.
So to fix the behavior of our NumString class, we have to change the return statements to our magic methods that affect this behavior to either return
self which would be considered mutable or a completely new instance of NumString which would be immutable. (Thanks Kenneth Love for your input!)
__iadd__ method would look like this then:
class NumString: # ... other methods ... def __iadd__(self, other): self.value = self + other return NumString(self.value)
Try making that one change, then testing the
ns1 += 8 and see what your type is.
Hope that helps, if not let me know.
Serdar Halac15,259 Points
Thank you so much. Really appreciate the effort to answer my question :) Clears it up.