[英]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_a
和side_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.