6

How to combine the following 2 classes into one class, Rectangle, so that a Rectangle object can be created either by rect = Rectangle(side_a, side_b) or rect = Rectangle(side_a, area)?

class Rectangle1:
    def __init__(self, side_a, side_b):
        self.side_a = side_a
        self.side_b = side_b
        self.area = self.side_a * self.side_b

class Rectangle2:
    def __init__(self, side_a, area):
        self.side_a = side_a
        self.area = area
        self.side_b = self.area / side_a
1
  • 1
    The interface you're asking for isn't possible, because there's no way to distinguish whether the second argument is a side length or an area. If creating rectangles from a side length and area is a common task in your use case, you may want to add a Rectangle.from_side_and_area static method. Commented Mar 15, 2017 at 20:36

5 Answers 5

11

As demonstrated here.

class Rectangle:

    def __init__(self, a, b):
        """ Create a new rectangle with sides of length a and b.
        """
        self.side_a = side_a
        self.side_b = side_b
        self.area = self.side_a * self.side_b

    @classmethod
    def from_sides(cls, a, b):
        return cls(a, b)

    @classmethod
    def from_area(cls, a, o):
        return cls(a, o/a)

You can then create rectangles as

r1 = Rectangle.from_sides(s1, s2)
r2 = Rectangle.from_area(s1, a)
Sign up to request clarification or add additional context in comments.

Comments

4

You cannot overload methods with methods of the same name. Well, you can, but only the last one is then visible.

The other option is keyword-only arguments.

With Python 3, you could write:

class Rectangle:
    def __init__(self, side_a, *, side_b=None, area=None):
        self.side_a = side_a
        if side_b is None and area is None:
            raise Exception("Provide either side_b or area")
        if side_b is not None and area is not None:
            raise Exception("Provide either side_b or area, not both")
        if side_b is not None:
            self.side_b = side_b
            self.area = self.side_a * self.side_b
        else:
            self.area = area
            self.side_b = self.area / side_a

using * in the middle forces the user to use keyword argument passing from that point, not allowing positionnal, which prevents the mistakes. And the (rather clumsy) manual checking for None logic ensures that one and only one keyword parameter is passed to the constructor. The inside is complex, but the interface is safe to use, that's the main point here.

r = Rectangle(10,area=20)
r2 = Rectangle(10,side_b=20)
r3 = Rectangle(10,20)   # doesn't run, need keyword arguments

3 Comments

s/ keyword / keyword-only
The correct term for these arguments is keyword-only arguments.
This is a very neat solution. Thanks.
0

This may not be what you're looking for, but here's what I came up with:

class Rectangle:
    def __init__(self, side_a, side_b = None, area = None):
         self.side_a = side_a
         if area == None:
            self.area = side_a * side_b
            self.side_b = side_b
         else:
            self.side_b = area / side_a
            self.area = area

5 Comments

Like I said abovE: Careful with this. Keyword arguments are still positional and you can legally call this function with neither side_b or area
marisbest2 - that's a good point, however I like to believe that someone using their own code wouldn't do that. Perhaps a if side_b = None and area = None would help
Never assume that the person using the code is the same person who wrote the code.
That sounds like good practice, actually. Thanks for the advice!
Even if you are the one who uses the code that you wrote, its unlikely that you'll remember weird edge cases
0

You could do

class Combined:
    def __init__(self, a, b=None, area=None):
        self.a = a
        self.area = self.a * b if b else area
        self.b = self.area / self.a

Comments

0

I would share a basic example to use default as well as parametrized constructor in python. You need to declare as well as assign variable in function definition itself for default constructor.

class Person:

    def __init__(self,name="",age=0):
        self.name=name
        self.age=age
    def __int__(self,name,age):
        self.name=name
        self.age=age

    def showdetails(self):
        print("\nName is "+str(self.name)+" and age is "+str(self.age))

p1=Person("Sam",50)

p1.showdetails()

p2=Person()

p2.showdetails()

Output:

Name is Sam and age is 50

Name is  and age is 0

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.