Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/css/37.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我如何告诉python类方法引用它自己的字段?_Python - Fatal编程技术网

我如何告诉python类方法引用它自己的字段?

我如何告诉python类方法引用它自己的字段?,python,Python,这是一个对我来说很难用语言表达的简单问题,因为我不太熟悉Python的语法。我有一个名为“四边形”的类,需要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("P

这是一个对我来说很难用语言表达的简单问题,因为我不太熟悉Python的语法。我有一个名为“四边形”的类,需要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,以及是否有更简单的方法来计算四边形的周长?

这看起来是一个近乎完美的问题,但我还是要进行编辑

要在开始时注意:

  • 尽可能避免上课
  • 试着少写垃圾代码/支持代码/样板代码,多写有趣的代码
  • 在创建新的东西之前,回顾python提供的任何东西
  • 至少编写一个小测试或一个
    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
作为练习,您可以创建父类多边形并从中继承四边形


更新:在最初的回答中,我写道:

# COMMENT: class Point better be a namedtuple, please google it and use it
对于一位尊敬的读者来说,这听起来过于直截了当,他指出:

点是否可变取决于您想在应用程序中对点执行什么操作。如果您没有任何理由不这样做,通常默认使用immutable

换句话说,除非您确定数据结构不应更改,否则谨慎的做法是不要匆忙使用
namedtuple
。对我来说,这听起来过于保护,但您不能否认,您对数据结构的选择取决于您计划如何使用它

要将某些内容转换为
namedtuple
,请参阅上的重构示例

其他建议的思考方向是使用标准库中提供的
dataclass

这就是您的意思吗

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)

我建议改变你记录自己观点的方式。使用列表,而不是名称中有四个单独的数字属性。然后,您可以将索引传递给
边长
方法:

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))

我还建议您将
重命名为\u string
函数重命名为
\uu str\uuu
,因为Python在某些情况下会自动为您调用该方法(例如,当您打印对象时)。

不要单独定义类变量和实例变量:在您的实例变量中,只需使用一个或另一个(它们从
self
开始)工作正常。这允许您使用
self来访问实例变量。这里有一些变量。\uuuuu
您已经有了
self
,所以只需从
self
获取它。作为旁注,不要定义
到字符串
的方法,而必须执行
打印(q.to\u string())
在任何地方,只要定义一个
\uuuu str\uuuu
方法;然后就可以
打印(q)
。更好的是,这意味着您可以使用字符串格式将字符串组合在一起。例如,
quartaleral.\uuu str\uuuu
可以
返回f'
,这将与
返回'
做相同的事情。为了补充JacobiRR的观点,有时您确实希望使用类属性作为实例属性的默认值但是在这种情况下,您希望它们实际上是有用的默认值。拥有一个应该是
实例的属性,但其回退类属性值是
0
,而不是
,这只是自找麻烦。无论如何,“偶尔”这里不适用您已经有了一个
\uuuu init\uuuu
,它已经有并存储了默认值,所以这就是您所需要的。我不认为所有这些建议都是好的。使用默认值的初始值设定项有一个可以用
point()构造的“零点”没有什么错
当然不是无稽之谈。此外,您确实需要先确定点、四边形等是可变的还是不可变的,然后再决定如何实现它们。
namedtuple
如果您希望不可变,但不可变,则有意义。如果
Point
namedtuple
(或
@dataclass
@attr.s
或…)类层次结构的问题是另一个问题,只有在确定了可变性之后才应该出现,例如,对于不可变值,矩形是四边形的子类型,而正方形是矩形的子类型,但是对于可变值,这是不正确的。@abarnert确实存在相当多的附加点矩形Poligon主题,但快速查看您的点:1)默认值是可以的,但在Point()上强制一个值更好,除非您想在某些宇宙中停留在(0,0)2)给定一个可变或不可变的选择,
宁愿不是3)
四边形
没有显式的setter方法,您可以假装它是不可变的4)
@dataclass
最好等到python 3.71)您在
(0,0)
上不会像在
上一样被“卡住”
,因为这是
str()返回的默认值
。2)
Point
是否可更改取决于您想对应用程序中的点执行什么操作。如果您没有任何理由不这样做,通常默认情况下使用immutable,但对于
四边形