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 trial

Python Object-Oriented Python Advanced Objects Math

tariqasghar
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
tariqasghar
UX Design Techdegree Graduate 30,519 Points

"iadd" method question

In the video, Kenneth needed to return self.value after setting it in the "iadd" function. Why is this necessary?

I was testing this on the workspace and if I don't return self.value in "iadd" function, and just set it, and then print the NumString object, it returns "None".

Thanks.

4 Answers

Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,716 Points

Ok... thinking this through I do want to change Kenneth's version slightly. At the end of the day the numerical calculations are the same, but Kenneth's version of __iadd__ forces the NumString to change to a new type, and the version I would write would keep its type.

But this is nitpicking because the internal representation self.value becomes converted anyway.... hehehe.

class NumString:
    def __init__(self, s):
        self.value = s
    def __repr__(self):
        return str(self.value)
    def __float__(self):
        return float(self.value)
    def __int__(self):
        return int(self)
    def __add__(self, other):
        return float(self.value) + other
    def __radd__(self, other):
        return float(self.value) + float(other)
    def __iadd__(self, other):
        self.value = float(self.value) + other
        return self.value # I don't like this, see how it changes the type of x below

x = NumString('3.14')
print("x has a type of: ", type(x))
print("x has a value of:", x)
# now, let's use the __add__ property of x
print("x + 1.1 has a type of: ", type(x + 1.1))
print("x + 1.1 has a value of:", x + 1.1)
# now, let's use the __iadd__ property of x
x += 2
print("x += 2 has a type of: ", type(x))
print("x += 2 has a value of:", x)

x += 10
print("x has a type of: ", type(x))
print("x has a value of:", x)

Output. Notice Kenneth's code changes the type of x to float

x has a type of:  <class '__main__.NumString'>
x has a value of: 3.14
x + 1.1 has a type of:  <class 'float'>
x + 1.1 has a value of: 4.24
x += 2 has a type of:  <class 'float'>
x += 2 has a value of: 5.140000000000001
x has a type of:  <class 'float'>
x has a value of: 15.14

But if you change the code to return self, you get a "mutable" version, which I think is better.

class NumString:
    def __init__(self, s):
        self.value = s
    def __repr__(self):
        return str(self.value)
    def __float__(self):
        return float(self.value)
    def __int__(self):
        return int(self)
    def __add__(self, other):
        return float(self.value) + other
    def __radd__(self, other):
        return float(self.value) + float(other)
    def __iadd__(self, other):
        self.value = float(self.value) + other
        return self # my modification

Note, the type of x stays as a NumString object with my modification.

x has a type of:  <class '__main__.NumString'>
x has a value of: 3.14
x + 1.1 has a type of:  <class 'float'>
x + 1.1 has a value of: 4.24
x += 2 has a type of:  <class '__main__.NumString'>
x += 2 has a value of: 5.140000000000001
x has a type of:  <class '__main__.NumString'>
x has a value of: 15.14
Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,716 Points

Great question! I like that you are thinking through these "magic methods" or "dunder methods" as some people call them. This concept slightly touches on the idea of mutable and immutable objects, but you'll learn more about that later.

__iadd__ is the "inplace addition" operator. It is different than the __add__ method which simply returns the RESULT of the addition to another object. But instead the __iadd__ is 2 operations with one operator... it evaluates first then and replaces itself with a new object.

Since the addition is "inplace" it means that the value of object is modified in place. Its counterparts are __isub__ and iconcat for strings.

x = 3
# the iadd adds and modifies the object, 2 operations in one!
x += 1
print(x)
Hank Tao
Hank Tao
7,540 Points

Also, since Kenneth initialised self.value to type str, shouldn't we keep self.value to type str in the iadd method. i.e.: self.value = str(self + other)

Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,716 Points

Hank, that is a valid observation. Typically you want consistency of types unless there is an explicit type conversion requested.

TL;DR

I wrote a class that was surprisingly similar for a Geo-telemetry piece. Streams of timestamped latitude and longitude coordinates were downloaded from several different GPS devices (that came from different manufacturers using different formats.) The class would recognize the feed format and serialize the differential ouput to a particular GPS standard output. But I would preserve the original data stream and serialized it to disk-- if anyone needed to prove the veracity of the data, it would be a nearly exact match to the data collected by the GPS devices.

Since we kept the types consistent, the original data could always be produced if anyone needed to assay or reproduce the results.