覆盖需要在__init __()中计算的子类中的变量的正确方法是什么?

时间:2016-10-25 15:39:57

标签: python oop inheritance

我正在学习Python中的继承机制,我想制作一些在子__init_()函数中自动计算的变量,但问题是我想要制作另一个变量将被覆盖的类。我不希望这些变量计算两次,一次在父级,然后在子级。那么,下面的例子是否正确?

class Shape(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def getcenter(self):
        return self.x, self.y


class Square(Shape):
    def __init__(self, x, y, a):
        super().__init__(x,y)
        self.a = a
        self.perimeter = 4*a
        self.field = a*a


class Rectangle(Square):
    def __init__(self, x, y, a, b):
        super().__init__(x, y, a)
        self.perimeter = (2*a) + (2*b)
        self.field = a*b

3 个答案:

答案 0 :(得分:3)

好吧,忽略你的上面实际上有更好的Square子类Rectangle,通常这种类型的逻辑将被移动到由子类重写的新函数。

class Square:
   def __init__(self, a):
      self.a = a
      self.calculate_perimeter()

   def calculate_perimeter(self):
      self.perimeter = self.a * 4


class Rectangle(Square):
   def __init__(self, a, b):
      self.b = b
      super().__init__(a)

   # automatically called by the parent class
   def calculate_perimeter(self):
      self.perimeter = self.a * 2 + self.b * 2

答案 1 :(得分:2)

我可以在这里想到几种不同的方法:

有人会问" Isn是一个矩形的特定情况吗?"

然后这应该有效,对吧?

class Rectangle(Shape):
    def __init__(self, x, y, a, b):
        super(Rectangle, self).__init__(x, y)
        self.perimeter = (2 * a) + (2 * b)
        self.field = a * b


class Square(Rectangle):
    def __init__(self, x, y, a):
        super(Square, self).__init__(x, y, a, a)
        self.a = a

这样,您就可以获得使用a=ab=a计算的周边字段属性。 Square 矩形

另一个不错的选择是使用@property装饰器,并动态计算周边字段(乘以整数是一个非常便宜的操作)

class Shape(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def getcenter(self):
        return self.x, self.y


class Square(Shape):
    def __init__(self, x, y, a):
        super(Square, self).__init__(x, y)
        self.a = a

    @property
    def perimeter(self):
        print("Perimeter of Square")
        return 4 * self.a

    @property
    def field(self):
        return self.a * self.a


class Rectangle(Square):
    def __init__(self, x, y, a, b):
        super(Rectangle, self).__init__(x, y, a)
        self.b = b

    @property
    def perimeter(self):
        print("Perimeter of Rectangle")
        return (2 * self.a) + (2 * self.b)

    @property
    def field(self):
        return self.a * self.b


if __name__ == "__main__":
    s = Square(1, 2, 3)
    r = Rectangle(5, 6, 7, 8)
    print ("Perimeter (square) %s" % s.perimeter)
    print ("Perimeter (rectangle) %s" % r.perimeter)

有关this great SO thread上的属性的更多信息。

还有更多!我建议你玩一些示例,看看你能做些什么(总的来说,你玩得很开心): - )

答案 2 :(得分:2)

您应该将它们作为属性实现,而不是制作perimeterfield属性。这样,它们只在被要求时计算。这也将防止在实例化类时必须计算这些值。

如前所述,从Rectangle继承Square非常有用。原则上,Square可能只是Rectangle的替代构造函数。虽然您可以根据使用情况选择不同的设计。

class Rectangle(Shape):
    def __init__(self, x, y, a, b):
        self.a = a
        self.b = b
        super().__init__(x, y)
    @property
    def perimeter(self):
        return (self.a + self.b) * 2


class Square(Rectangle):
    '''Just an alternate constructor for Rectangle'''
    def __init__(self, x, y, a):
        super().__init__(x, y, a, a) #b is same as a in a square.
    #everything else already works!

示例:

>>> my_square = Square(0, 0, 10)
>>> my_square.perimeter
40
>>> my_rect = Rectangle(0,0, 10, 5)
>>> my_rect.perimeter
30

属性解决的另一个问题是用户不能错误地将周长设置为不同的值。

>>> my_square.perimeter = 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

否则,perimeter将被允许设置,因此与ab

的值不一致
#without properties you could get inconsistent values
>>> my_square.perimeter = 10
>>> print(my_square.perimeter, my_square.a, my_square.b)
10 10 10

您还可以使用属性设置器来指示当有人尝试设置属性时会发生什么。

class Circle(object):
    def __init__(self, radius):
        self.radius = radius

    @property
    def diameter(self):
        return self.radius * 2

    @diameter.setter
    def diameter(self, diam):
        self.radius = diam / 2

例如:

>>> my_circle = Circle(radius=5)
>>> my_circle.diameter
10
>>> my_circle.diameter = 40
>>> my_circle.radius
20
相关问题