简体   繁体   English

Python 继承和变量

[英]Python inheritence and variables

I'm going over python and inheritance and this is the code in question:我正在检查 python 和 inheritance ,这是有问题的代码:

class Shape:
    def __init__(self, area=0, perimeter=0):
        self.area = area
        self.perimeter = perimeter

    def _calc_area(self, side_a, side_b, side_c):
        pass

    def _calc_perimeter(self, side_a, side_b, side_c):
        pass

    @property
    def area(self):
        return self._area

    @area.setter
    def area(self, area):
        self._area = area

    @property
    def perimeter(self):
        return self._perimeter

    @perimeter.setter
    def perimeter(self, perimeter):
        self._perimeter = perimeter


class Triangle(Shape):
    def __init__(self, side_a, side_b, side_c):
        super().__init__(area=0, perimeter=0)
        self._side_a = side_a
        self._side_b = side_b
        self._side_c = side_c

        self._calc_area(self._side_a, self._side_b, self._side_c)
        self._calc_perimeter(self._side_a, self._side_b, self._side_c)

    def _calc_area(self, side_a, side_b, side_c):
        s = (side_a + side_b + side_c) / 2
        self.area = (s*(s-side_a)*(s-side_b)*(s-side_c)) ** 0.5

    def _calc_perimeter(self, side_a, side_b, side_c):
        self.perimeter = side_a + side_b + side_c

    @property
    def side_a(self):
        return self._side_a

    @property
    def side_b(self):
        return self._side_b

    @property
    def side_c(self):
        return self._side_c

    @side_a.setter
    def side_a(self, side_a):
        self._side_a = side_a

    @side_b.setter
    def side_b(self, side_b):
        self._side_b = side_b

    @side_c.setter
    def side_a(self, side_c):
        self._side_c = side_c


t = Triangle(1, 2, 3)
print(f"Triange: side_a = {t.side_a}, side_b = {t.side_b}, side_c = {t.side_c}")
print(f"Triangle: area = {t.area}, perimenter = {t.perimeter}")

The parameter of t.side_a keeps getting set to 3 and that returns the wrong calculation for the triangle. t.side_a 的参数一直设置为 3,这会返回错误的三角形计算。 When I look at the variables when debugging, I'm getting this:当我在调试时查看变量时,我得到了这个:

在此处输入图像描述

As I step through the code, I see that the correct values are set for side_b=2 and side_c=3, but side_a keeps getting 3, although the input is 1, as the image shows.当我单步执行代码时,我看到为 side_b=2 和 side_c=3 设置了正确的值,但 side_a 一直为 3,尽管输入为 1,如图所示。 Am I missing something?我错过了什么吗?

The problem is that you have a typo: you decorated a function named side_a , not side_c , with @side_c.setter , so that now both side_a and side_c refer to the same property.问题是你有一个错字:你装饰了一个名为side_a的 function ,而不是side_c ,使用@side_c.setter ,所以现在side_aside_c都引用相同的属性。

However, this is an easy typo to make if you are copy-and-pasting lots of similar properties.但是,如果您要复制和粘贴许多类似的属性,则很容易出现这种错误。 You can avoid this by defining a custom descriptor to abstract away the common code for all three sides.您可以通过定义自定义描述符来抽象出所有三个方面的公共代码来避免这种情况。

class Side:
    def __get__(self, obj, type_):
        if obj is None:
            return self
        return getattr(obj, self.name)

    def __set__(self, obj, value):
        setattr(obj, self.name, value)
        # We're assuming this is used with subclasses of Shape
        # Changing a side invalidates whatever area or perimeter
        # may have been cached.
        obj._area = None
        obj._perimeter = None

    def __set_name__(self, owner, name):
        # E.g., if you write side_a = Side(), then
        # this method sets the name attribute of the descriptor
        # to "_side_a". This gets used to access the appropriate
        # attribute of an instance in __get__ and __set__
        self.name = "_" + name

Before we show how Side is used, let's clean up Shape .在我们展示如何使用Side之前,让我们清理一下Shape

# Area and perimeter are *computed* values, based on other mutable
# properties of a shape. You shouldn't be able to set them directly.
class Shape:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._area = None
        self._perimeter = None

    @property
    def area(self):
        if self._area is None:
            self._area = self._calc_area()
        return self._area

    @property
    def perimeter(self):
        if self._perimeter is None:
            self._perimeter = self._calc_perimeter()
        return self._perimeter

    # "Abstract" methods. Shape doesn't do anything to
    # to implement them, but let's emphasize that they
    # should be overriden in subclasses. Use of the abc
    # module is beyond the scope of this module.

    def _calc_area(self):
        pass

Now let's define a subclass to take advantage of Side :现在让我们定义一个子类来利用Side

class Triangle(Shape):
    side_a = Side()
    side_b = Side()
    side_c = Side()

    def __init__(self, a, b, c, **kwargs):
        super().__init__(**kwargs)
        self.side_a = a
        self.side_b = b
        self.side_c = c

    # The triangle-specific definitions of _calc_area
    # and _calc_perimeiter that the area and perimeter
    # properties expect.

    def _calc_area(self):
        s = (self.side_a + self.side_b + self.side_c) / 2
        return (s*(s-self.side_a)*(s-self.side_b)*(s-self.side_c)) ** 0.5

    def _calc_perimeter(self):
        return self.side_a + self.side_b + self.side_c

You can also define a simpler function that just creates a property , though it requires you to specify a name to use explicitly ( property doesn't use the __set_name__ method).您还可以定义一个更简单的 function ,它只创建一个property ,尽管它要求您明确指定要使用的名称( property不使用__set_name__方法)。

def make_side(name):
    attr = "_" + name

    def get(self):
        return getattr(self, attr)

    def set(self, value):
        setattr(self, attr, value)
        self._area = None
        self._perimeter = None

    return property(get, set)

class Triangle(Shape):
    side_a = make_side("side_a")
    side_b = make_side("side_b")
    side_c = make_side("side_c")

    ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM