我什么时候应该在 Python 中使用类和 self 方法?

MLa*_*paj 3 python oop

我一直在尝试编写一个 Python 程序来根据与 4 个锚点的距离计算点位置。我决定将其计算为 4 个圆的交点。

我有一个关于不是算法的问题,而是关于在此类程序中使用类的问题。我真的没有太多的 OOP 经验。真的有必要在这里使用类还是至少以任何方式改进程序?

这是我的代码:

import math

class Program():
    def __init__(self, anchor_1, anchor_2, anchor_3, anchor_4, data):
        self.anchor_1 = anchor_1
        self.anchor_2 = anchor_2
        self.anchor_3 = anchor_3
        self.anchor_4 = anchor_4



    def intersection(self, P1, P2, dist1, dist2): 
        PX = abs(P1[0]-P2[0])               
        PY = abs(P1[1]-P2[1])
        d = math.sqrt(PX*PX+PY*PY)   

        if d < dist1+ dist2 and d > (abs(dist1-dist2)):

            ex = (P2[0]-P1[0])/d
            ey = (P2[1]-P1[1])/d

            x = (dist1*dist1 - dist2*dist2 + d*d) / (2*d)
            y = math.sqrt(dist1*dist1 - x*x)

            P3 = ((P1[0] + x * ex - y * ey),(P1[1] + x*ey + y*ex))
            P4 = ((P1[0] + x * ex + y * ey),(P1[1] + x*ey - y*ex))  

            return (P3,P4)
        elif d == dist1 + dist2:
            ex = (P2[0]-P1[0])/d
            ey = (P2[1]-P1[1])/d

            x = (dist1*dist1 - dist2*dist2 + d*d) / (2*d)
            y = math.sqrt(dist1*dist1 - x*x)

            P3 = ((P1[0] + x * ex + y * ey),(P1[1] + x*ey + y*ex))

            return(P3, None)
        else:
            return (None, None)





    def calc_point(self, my_list):
        if len(my_list) != 5:
            print("Wrong data")
        else:
            tag_id = my_list[0];
            self.dist_1 = my_list[1];
            self.dist_2 = my_list[2];
            self.dist_3 = my_list[3];
            self.dist_4 = my_list[4];

        (self.X1, self.X2) = self.intersection(self.anchor_1, self.anchor_2, self.dist_1, self.dist_2)
        (self.X3, self.X4) = self.intersection(self.anchor_1, self.anchor_3, self.dist_1, self.dist_3)
        (self.X5, self.X6) = self.intersection(self.anchor_1, self.anchor_4, self.dist_1, self.dist_4)





with open('distances.txt') as f:
    dist_to_anchor = f.readlines()

dist_to_anchor = [x.strip() for x in dist_to_anchor]
dist_to_anchor = [x.split() for x in dist_to_anchor]
for row in dist_to_anchor:
    for k in range(0,5):
        row[k] = float(row[k])
anchor_1= (1,1)
anchor_2 = (-1,1)
anchor_3 = (-1, -1)
anchor_4 = (1, -1)

My_program = Program (anchor_1, anchor_2, anchor_3, anchor_4, dist_to_anchor)
My_program.calc_point(dist_to_anchor[0])

print(My_program.X1)
print(My_program.X2)
print(My_program.X3)
print(My_program.X4)
print(My_program.X5)
print(My_program.X6)
Run Code Online (Sandbox Code Playgroud)

另外,我不太明白我应该在哪里使用 self 关键字以及在哪里不需要它。

Sch*_*ern 9

真的有必要在这里使用类或者它至少以任何方式改进程序吗?

类从来都不是必需的,但它们对于组织代码通常非常有用。

就您而言,您已经获取了过程代码并将其包装在一个类中。它基本上仍然是一堆函数调用。您最好将编写为过程编写适当的类。


让我们看看如何以程序风格面向对象风格来处理一些几何图形。

过程式编程就是编写函数(过程)来获取一些数据、处理它并返回一些数据。

def area_circle(radius):
    return math.pi * radius * radius

print(area_circle(5))
Run Code Online (Sandbox Code Playgroud)

你有了圆的半径,就得到了面积。

面向对象编程是要求数据做事。

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

    def area(self):
        return math.pi * self.radius * self.radius

circle = Circle(radius=5)
print(circle.area())
Run Code Online (Sandbox Code Playgroud)

你有一个圆,你求它的面积。

这似乎需要很多额外的代码来实现非常微妙的区别。何必呢?

如果您需要计算其他形状会怎样?这是 OO 中的一个 Square。

class Square():
    def __init__(self, side=0):
        self.side = side
    
    def area(self):
        return self.side * self.side

square = Square(side=5)
print(square.area())
Run Code Online (Sandbox Code Playgroud)

现在是程序化的。

def area_square(side):
    return side * side
print(area_square(5));
Run Code Online (Sandbox Code Playgroud)

所以呢?当您想要计算形状的面积时会发生什么?从程序上讲,任何想要处理形状的人都必须知道它正在处理哪种形状、调用什么过程以及从哪里获取该过程。该逻辑可能分散在整个代码中。为了避免这种情况,您可以编写一个包装函数并确保根据需要导入它。

from circle import 'area_circle'
from square import 'area_square'

def area(type, shape_data):
    if type == 'circle':
        return area_circle(shape_data)
    elif type == 'square':
        return area_square(shape_data)
    else:
        raise Exception("Unrecognized type")

print(area('circle', 5))
print(area('square', 5))
Run Code Online (Sandbox Code Playgroud)

在 OO 中你可以免费得到它。

print(shape.area())
Run Code Online (Sandbox Code Playgroud)

无论shape是 aCircle还是 a Squareshape.area()都可以。您,使用该形状的人,不需要知道它是如何工作的。如果您想对形状进行更多操作,例如计算周长,请perimeter向形状类添加一个方法,现在无论您有形状,都可以使用该方法。

随着更多形状的添加,程序代码在需要使用形状的地方变得越来越复杂。OO 代码保持完全相同,只是编写更多的类。

这就是面向对象的要点:将工作如何完成的细节隐藏在界面后面。只要结果相同,代码如何工作并不重要。


Ism*_*sma 5

恕我直言,类和 OOP 总是一个不错的选择,通过使用它们,您将能够更好地组织和重用您的代码,您可以创建从现有类派生的新类以扩展其功能(继承)或更改其行为,如果您需要它(多态性)以及封装代码的内部结构,以便它变得更安全(尽管在 Python 中没有真正的封装)。

例如,在您的特定情况下,您正在构建一个计算器,该计算器使用一种技术来计算交集,如果其他人使用您的类想要修改该行为,他们可以覆盖该函数(这是多态性的作用):

class PointCalculator:
    def intersection(self, P1, P2, dist1, dist2): 
        # Your initial implementation

class FasterPointCalculator(PointCalculator):
    def __init__(self):
        super().__init__()

    def intersection(self, P1, P2, dist1, dist2):
        # New implementation
Run Code Online (Sandbox Code Playgroud)

或者,您将来可能会扩展该类:

class BetterPointCalculator(PointCalculator):
        def __init__(self):
            super().__init__()

        def distance(self, P1, P2):
            # New function
Run Code Online (Sandbox Code Playgroud)

您可能需要使用一些必需的数据初始化您的类,并且您可能不希望用户能够修改它,您可以通过使用下划线命名变量来表示封装:

class PointCalculator:
    def __init__(self, p1, p2):
        self._p1 = p1
        self._p2 = p2

    def do_something(self): 
        # Do something with your data
        self._p1 + self._p2
Run Code Online (Sandbox Code Playgroud)

您可能已经注意到,调用函数时会自动传递 self,它包含对当前对象(类的实例)的引用,因此您可以访问其中声明的任何内容,例如上面示例中的变量 _p1 和 _p2。

您还可以创建类方法(静态方法),然后您无法访问 self,您应该为执行一般计算或任何不需要特定实例的操作的方法执行此操作,您的交集方法可能是一个很好的方法候选人例如

class PointCalculator:

    @staticmethod
    def intersection(P1, P2, dist1, dist2): 
        # Return the result
Run Code Online (Sandbox Code Playgroud)

现在您不需要 PointCalculator 的实例,您只需调用 PointCalculator.intersection(1, 2, 3, 4)

使用类的另一个好处可能是内存优化,当对象超出范围时,Python 会从内存中删除对象,所以如果你有一个包含大量数据的长脚本,在脚本终止之前它们不会从内存中释放。

话虽如此,对于执行非常特定任务的小型实用程序脚本,例如,安装应用程序、配置一些服务、运行一些操作系统管理任务等......一个简单的脚本完全没问题,这也是 Python 的原因之一如此受欢迎。