问题
I'm creating a simple angle object which is, in it's simplest forms, an integer value that recurs when it goes above 360 (back to 0) or below 0 (back to 360). I implemented the magic methods for augmented assignment and for some reason this isn't working.
class Angle:
def __init__(self, angle):
self.angle = angle
def __add__(self, other):
"""Check for recursion, then return the added value."""
val = self.angle + other
while val > 360:
val -= 360
while val < 0:
val += 360
return val
def __radd__(self, other):
"""Reflected addition."""
return self.__add__(other)
def __iadd__(self, other):
"""Augmented addition assignment."""
val = self.__add__(other)
self.angle = val
It assigns a nonetype value to self.angle,=. It seems to me like it should work, since self.__add__() returns the value of other added to self.angle, so why wouldn't it assign val to self.angle?
回答1:
About __iadd__ (and other __iXXX__ methods):
These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self).
Your __iadd__ method doesn't "assigns a nonetype to self.angle", it returns None (default value when a function has no explicit return), and this rebinds your current name to None, ie:
class Foo(object):
def __init__(self, angle=120):
self.angle = angle
def __add__(self, other):
"""Check for recursion, then return the added value."""
print "Foo.add"
val = self.angle + other
while val > 360:
val -= 360
while val < 0:
val += 360
return val
def __iadd__(self, other):
"""Augmented addition assignment."""
print "Foo.iadd"
val = self.__add__(other)
self.angle = val
f = Foo()
f += 20
print f is None
To make a long story short : you want __iadd__ to return something meaningfull, most probably self.
回答2:
Both __add__ and __iadd__ should return an Angle object:
class Angle(object):
def __init__(self, angle):
self.angle = angle % 360
def __add__(self, other):
return Angle(self.angle + other.angle)
def __iadd__(self, other):
return self.__add__(other)
Note use of angle % 360 to keep angle in range(0, 360).
回答3:
Just delete your __iadd__ method completely.
This will make += operator fall back onto __add__, which is the preferred behaviour for objects which model immutable types.
Example:
class Angle(int):
def __init__(self, val):
self.val = val % 360
def __add__(self, other):
return Angle(self.val + other)
def __sub__(self, other):
return Angle(self.val - other)
def __neg__(self):
return Angle(-self.val)
def __repr__(self):
# just for demonstration purposes ok
return str(self.val)
Let's try it out...
>>> a = Angle(35)
>>> a
35
>>> a + 400
75
>>> a
35
>>> a += 400
>>> a
75
>>> a - 75
0
>>> a - 76
359
>>> a
75
>>> a -= 76
>>> a
359
No __iadd__ or __radd__ is needed, let python handle the magic for you.
回答4:
You need to return self from the implementation of __iadd__ since the return value of __iadd__ is assigned to the left hand side of the += operator. This is what the Python documentation says about the implementation of in-place operators:
These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self).
(source)
来源:https://stackoverflow.com/questions/20977179/python-magic-method-augmented-assignment-misunderstanding