这是一个简单的问题,我很难用语言表达,因为我对Python的语法不太熟悉。我有一个名为“Quadrilateral”的类需要4个点,我正在尝试创建一个名为“side_length”的方法,我想用它来计算四边形上两个顶点之间的直线长度:
import math
class Point:
x = 0.0
y = 0.0
def __init__(self, x=0, y=0):
self.x = x
self.y = y
print("Point Constructor")
def to_string(self):
return "{X: " + str(self.x) + ", Y: " + str(self.y) + "}"
class Quadrilateral:
p1 = 0
p2 = 0
p3 = 0
p4 = 0
def __init__(self, p1=Point(), p2=Point(), p3=Point(), p4=Point()):
self.p1 = p1
self.p2 = p2
self.p3 = p3
self.p4 = p4
print("Quadrilateral Constructor")
def to_string(self):
return "{P1: " + self.p1.ToString() + "}, " + "{P2: " + self.p2.ToString() + "}, " + \
"{P3: " + self.p3.ToString() + "}," + "{P4: " + self.p4.ToString() + "}"
def side_length(self, p1, p2):
vertex1 = p1
vertex2 = p2
return math.sqrt((vertex2.x - vertex1.x)**2 + (vertex2.y - vertex1.y)**2)
def perimeter(self):
side1 = self.side_length(self.p1, self.p2)
side2 = self.side_length(self.p2, self.p3)
side3 = self.side_length(self.p3, self.p4)
side4 = self.side_length(self.p4, self.p1)
return side1 + side2 + side3 + side4
现在我通过明确告诉它使用四边形的点来调用side_length方法,但有没有办法隐式使用“p1”和“p2”而不需要告诉它使用四边形的点(当我只想使用p1和p2并暗示python使用四边形的点时,我正在使用q.p1和q.p2)?我已经意识到它基本上是一个静态方法,我希望它使用类字段而不是任何一点。
q = Quadrilateral(p1, p2, p3, p4)
print(q.to_string())
print("Side length between " + q.p1.to_string() + " and " + q.p2.to_string() + ": " + str(q.side_length(q.p1, q.p2)))
print("Perimeter is: " + str(q.perimeter()))
我还有其他冗余问题 - 比如有没有更好的方法来最初定义四边形类中的点p1,p2,p3,p4,是否有更简单的方法来计算四边形的周长?
答案 0 :(得分:0)
对于https://codereview.stackexchange.com来说,这看起来几乎是一个完美的问题,但无论如何我都会进行编辑。
在开始时注意:
assert
以下是我对您的代码的编辑,希望您找到一些有用的想法/线索:
# not needed
#import math
class Point:
#FIXME: delete this and do some reading on class attributes
# x = 0.0
# y = 0.0
def __init__(self, x, y):
self.x = x
self.y = y
#FIXME: use __str__ method for this
# def to_string(self):
# return "{X: " + str(self.x) + ", Y: " + str(self.y) + "}"
def distance(self, p):
return ((self.x - p.x) ** 2 + (self.y - p.y) ** 2) ** .5
class Quadrilateral:
# FIXME: same as in class Point
# p1 = 0
# p2 = 0
# p3 = 0
# p4 = 0
# FIXED: you do not want anything undefined for constructor
# def __init__(self, p1=Point(), p2=Point(), p3=Point(), p4=Point()):
def __init__(self, p1, p2, p3, p4):
# saving some typing
self.points = [p1, p2, p3, p4]
# self.p2 = p2
# self.p3 = p3
# self.p4 = p4
# print("Quadrilateral Constructor")
# FIXME: please make some life easier
# def to_string(self):
# return "{P1: " + self.p1.ToString() + "}, " + "{P2: " + self.p2.ToString() + "}, " + \
# "{P3: " + self.p3.ToString() + "}," + "{P4: " + self.p4.ToString() + "}"
def side(self, vertex_n):
# TODO: elaborate some protection against wrong *vertex_n*
a, b = self.points[vertex_n], self.points[vertex_n-1]
return a.distance(b)
@property
def perimeter(self):
return sum([self.side(i) for i in range(4)])
# any duplicated code is a sign of danger: somehtign is going wrong!
#side1 = self.side_length(self.p1, self.p2)
#side2 = self.side_length(self.p2, self.p3)
#side3 = self.side_length(self.p3, self.p4)
#side4 = self.side_length(self.p4, self.p1)
#return side1 + side2 + side3 + side4
pts = [Point(x, y) for x, y in [(0,0), (1,0), (1,1), (0,1)]]
assert Quadrilateral(*pts).perimeter == 4
作为练习,您可以创建父类Polygon并从中继承Quadrilateral。
更新:在我写的原始答案中:
# COMMENT: class Point better be a namedtuple, please google it and use it
对于一位受人尊敬的读者来说,这听起来过于绝对,他指出:
Point是否可变取决于您想要在应用中使用点数做什么。如果您没有任何理由不这样做,通常默认使用不可变。
换句话说,除非您确定数据结构不应该改变,否则不要急于namedtuple
。对我来说听起来过于保护,但你不能否认你选择数据结构的逻辑取决于你打算如何使用它。
要将某些内容转换为namedtuple
,请在Raymond Hettinger - Beyond PEP 8 -- Best practices for beautiful intelligible code查看一个很好的重构示例。
其他建议的方向是使用dataclass
,可在标准库中找到Python 3.7。
答案 1 :(得分:0)
这是你的意思吗?
def side_length(self, side):
if side == 1:
vertex1 = self.p1
vertex2 = self.p2
elif side == 2:
vertex1 = self.p2
vertex2 = self.p3
elif side == 3:
vertex1 = self.p3
vertex2 = self.p4
elif side == 4:
vertex1 = self.p4
vertex2 = self.p1
else:
print("Error! No side {}".format(side))
return None
return math.sqrt((vertex2.x - vertex1.x)**2 + (vertex2.y - vertex1.y)**2)
答案 2 :(得分:0)
我建议您更改跟踪积分的方式。不要使用名称中包含数字的四个单独属性,而是使用列表。然后,您可以将索引传递给side_lengths
方法:
def __init__(self, p1, p2, p3, p4):
self.points = [p1, p2, p3, p4]
def side_length(self, n):
vertex1 = self.points[n]
vertex2 = self.points[n-1] # if the index becomes -1, that's ok, it will wrap around
return math.sqrt((vertex2.x - vertex1.x)**2 + (vertex2.y - vertex1.y)**2)
def perimeter(self):
return sum(side_length(i) for i in range(4))
我还建议您将to_string
函数重命名为__str__
,因为在某些情况下Python会自动为您调用该方法(例如当您打印对象时)。 / p>