为什么我应该在Python中使用类?

21

我是一位Python非常菜的学习者,最近开始学习类的概念。我大致理解了类的概念,但不明白为什么不能简单地编写一些函数而非编写一个类?

例如,(我正在从Interactive python学习)其中一个练习(需要使用类编写)是:

  1. 添加一个distanceFromPoint方法,与distanceFromOrigin类似,但它以Point作为参数,并计算该点与self之间的距离。

  2. Point添加一个reflect_x方法,返回一个新的Point,它是该点关于x轴的镜像。例如,Point(3, 5).reflect_x()(3, -5)

他们使用类编写代码如下:

import math

class Point:
    """ Point class for representing and manipulating x,y coordinates. """

def __init__(self, initX, initY):
    """ Create a new point at the given coordinates. """
    self.x = initX
    self.y = initY

def getX(self):
    return self.x

def getY(self):
    return self.y

def distanceFromOrigin(self):
    return ((self.x ** 2) + (self.y ** 2)) ** 0.5

def distanceFromPoint(self, otherP):
    dx = (otherP.getX() - self.x)
    dy = (otherP.getY() - self.y)
    return math.sqrt(dy**2 + dx**2)

p = Point(3, 3)
q = Point(6, 7)

print(p.distanceFromPoint(q))

当我可以像这样简单地编写它们时,为什么要使用类呢?

def distanceFromPoint(p,q): # they are tuples
    y = (p[0]-q[0])**(2)+(p[1]-q[1])**(2)
    return y**(1/2)

def reflectx(p):
    return (p[0],-p[1])

p = (3,3)
q = (6,7)

1
至少getXgetY方法不被视为 Pythonic 代码。你可以直接访问 xy 属性。 - Matthias
1
除了以下更一般的答案之外:你实际上并不需要 getX/getY。Python 没有真正的私有成员,因此只使用 x/y 是可以的。如果需要覆盖它们,则属性始终可用。 - viraptor
@Matthias: 我是Python的新手。我不明白什么是Pythonic代码,以及为什么getX和getY不是Pythonic代码? - user6038537
2
你不需要为什么是或不是“Pythonic”而烦恼,但我希望你能看到mypoint.xmypoint.getX()更简单、更清晰。然而,在这样一个简单的例子中很难说明面向对象编程的强大之处。直到我遇到了一个真正需要解决的问题,我才真正理解了它的威力,我突然意识到,如果我编写一个继承自别人先前定义的类的类,那么解决问题将变得非常简单。 - nekomatic
@nekomatic:你能否提及一个让你有“恍然大悟”时刻的Python面向对象编程练习? - user6038537
显示剩余2条评论
4个回答

22

使用面向对象编程的一个重要优点是可扩展性

假设您编写了一个处理大量点数据的应用程序。现在您的客户添加了需求,除了每个点的x和y坐标外,您的应用程序还需要知道每个点的颜色。

如果您编写代码来将每个点存储为元组(x, y),则可以将颜色作为第三个值添加:(x, y, colour)。但现在您必须检查所有代码以查找因更改数据格式而出现错误的位置。如果您使用类,则可以简单地定义一个继承自Point并添加所需功能的新类:

class ColouredPoint(Point):
    """ Class for points which are coloured, based on Point """

    def __init__(self, initX, initY, initCol):
        Point.__init__(self, initX, initY)
        self.colour = initCol

p = ColouredPoint(3, 3, "green")
q = ColouredPoint(6, 7, "red")
r = Point(8, 4)

print(p.distanceFromPoint(q))
print(p.colour)
print(p.distanceFromPoint(r))

所有与 Point 类一起使用的代码在新类中仍然可以正常工作,即使您没有编写或无法更改 Point 类的定义。


什么是破损的代码?一个没有类的函数不能忽略额外的值吗?例如,在不使用类的情况下,我可能会把一个字符串放在元组的p [2]上,而元组将在计算距离时忽略它。 - user6038537
没错,但是你在问题中的 reflectx 函数会丢失额外的数据。我相信你可以想出一个解决方案,但我希望你能看到,你想到的原始更多可能的更改和扩展,就越难设计一个始终正确行为的数据类型。当你涉及到真实世界应用程序的复杂性时,面向对象编程更容易、更健壮,而上述只是使用面向对象编程的原因之一。 - nekomatic
如果我总结类的需求为“高效地添加新功能(以操纵某些对象)或更改对象的结构”,那么我是否遗漏了什么?使用面向对象编程的其他原因是什么? - user6038537
1
除了可扩展性之外,面向对象编程还具有许多功能,例如: 1.组织:在代码中描述和定义数据和过程。 2.状态:面向对象编程帮助您定义和跟踪状态。 3.封装:过程和数据被存储在一起。 4.继承:继承允许您在一个地方(一个类中)定义数据和过程,然后稍后覆盖或扩展该功能。 5.可重用性:所有这些原因以及其他原因都允许更大程度的代码重用。面向对象的代码使您能够编写一次可靠(经过测试)的代码,然后一遍又一遍地重用它。 - kamran kausar

3
您可以在类外声明一个函数,但通常在编程中将它们存储在类中是更好的实践。面向对象编程被认为更易读且可重用。在这种情况下,两个点之间的距离取决于点,因此在该类中具有distanceFromPoint方法是合理的。类还允许您确保从Points而不是包含错误值(如超过两个坐标)的元组计算距离。

我可以在我的距离函数中定义一个检查(使用if函数),如果元组中的前两个数据是整数,则计算,否则显示错误。那么我为什么要使用类呢? - user6038537

1

你举的点的例子并不适合用来证明类的使用。

类是描述“某物是什么”而不是操纵数据的绝佳方式。所以一个点只有两个描述它的信息(至少在2D空间中是这样)。

想象一些更抽象的东西,比如电影文件。电影与许多信息相关联:标题、持续时间、流派、演员、年龄分级、受欢迎程度、语言、奖项...等等。试着编写一个处理所有这些信息的长列表的函数,你很快就会看到类的好处。

数学可能是唯一一个真正没有太多上下文信息的领域,只有少数几个变量就可以定义一切。现实世界的数据定义不够明确,因此从类的可扩展性中受益。


你能否详细说明一下你的第二段(一个类是一个伟大的...)?我刚开始学习Python,没有很多的例子,所以不太理解。 - user6038537

0

函数式面向对象编程是不同的范例。

在Python中,所有东西都是一个对象,甚至包括int。整个语言都朝着面向对象编程的方向发展,这就是为什么你应该优先选择它,特别是如果你的程序会不断增长。


4
你混淆了函数式编程命令式编程。 函数式编程并不意味着你只使用函数。 你可以在Python中进行函数式编程,但是文章中描述的方式是命令式编程 - Shiho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接