Python Magic Method Augmented Assignment misunderstanding

纵饮孤独 提交于 2019-12-14 04:08:48

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!