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
DYZ
  • 55,249
  • 10
  • 64
  • 93
zhuhuren
  • 337
  • 4
  • 7
  • 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. – user2357112 Mar 15 '17 at 20:36

5 Answers5

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)
Community
  • 1
  • 1
PidgeyUsedGust
  • 797
  • 4
  • 11
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
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
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
  • 1
    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 Mar 15 '17 at 20:41
  • 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 – Eric Swedlo Mar 15 '17 at 20:44
  • Never assume that the person using the code is the same person who wrote the code. – marisbest2 Mar 15 '17 at 20:45
  • That sounds like good practice, actually. Thanks for the advice! – Eric Swedlo Mar 15 '17 at 20:47
  • Even if you are the one who uses the code that you wrote, its unlikely that you'll remember weird edge cases – marisbest2 Mar 15 '17 at 20:47
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
Amin Etesamian
  • 3,363
  • 5
  • 27
  • 50
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
pushkin
  • 9,575
  • 15
  • 51
  • 95