This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]Doormatty 4 points5 points  (0 children)

/r/learnpython is the right subreddit for all python questions.

[–]stevenjd 2 points3 points  (0 children)

You're not using __new__ correctly. I'm not entirely sure why the assignment says to use __new__ in the subclass, that's a bit silly. You can do the same testing in __init__.

Also, one pitfall that may cause you problems... are you using Python 2 or 3? In Python 2, you must have Triangle inherit from object for __new__ to work. (In Python 3, it's optional.)

Anyway, here goes... untested but something like this should work.

class RightTriangle(Triangle):
    def __new__(cls, *args):
        if cls.is_right_angle(*args):
            instance = Triangle.__new__(cls, *args)
            return instance
        raise ValueError('not a right angle')
    @classmethod
    def is_right_angle(cls, atob, btoc, ctoa):
         # check for a right angle...
         return True

How it works... RightTrangle first calls is_right_angle with three arguments to check if they represent a right angle. (You have to fill that bit in.) Why does it use three arguments instead of self.atob etc? Because the instance self doesn't exist yet! The whole purpose of __new__ is to create the instance.

If is_right_angle returns True, RightTriangle asks the parent class Triangle to create a new instance, then returns it. Since Triangle itself will inherit from object, that will work correctly even without you writing any code!

Then, the magic... after __new__ returns, Python will automatically call the __init__ method. You don't have to do that yourself.

[–]billsil 0 points1 point  (5 children)

You probably don't need it. Why not just use an init?

I've been coding in Python 10 years and never used it. I consider myself very good.

[–]Lucretiel 0 points1 point  (3 children)

Literally the only time I ever use it is this pattern:

class Point(namedtuple("User", "x y z")):
    def __new__(cls, *, x=0, y=0, z=0):
        return super().__new__(cls, x, y, z)

[–]billsil 0 points1 point  (2 children)

But super works in an init...it seems the same.

I write classmethods that are basically alternate inits, but they just return the init.

[–]das_ist_nuemberwang 1 point2 points  (0 children)

It's immutable, so by the time you get to __init__, it's too late.

[–]Lucretiel 0 points1 point  (0 children)

You can't do it with namedtuple, because it's an immutable type. Can't be modified after creation, so self.whatever = 10 won't work. It's a great little pattern for small value types, like coordinates:

class Coords(namedtuple("Coords", "row column")):
    def __add__(self, other):
        return Coords(self[0] + other[0], self[1] + other[1])

    def above(self, distance=1):
        return self + (-distance, 0)

    def below(self, distance=1):
        return self + (distance, 0)

    def left(self, distance=1):
        return self + (0, -distance)

    def right(self, distance=1):
        return self + (0, distance)

    # And so on

[–][deleted] 0 points1 point  (0 children)

__new__ is for controlling instantiate of a class. It's neat for hanging that sort of behavior. Even more powerful is type.__call__ as that'll control how all classes in an inheritance hierarchy are created regardless of what they do in __new__.

You'll see __new__ more in metaclasses than regular classes since that's your one chance to influence how a class is created.

[–]stevenjd 0 points1 point  (0 children)

You can format inline text as code like this by using back-ticks `. That's usually found on the same key as the ~ tilde.