在编程中,抽象是什么意思?

57
我正在学习Python,并且不确定是否理解了以下语句:“函数(包括其名称)可以捕获我们对问题的心理分块或抽象。”。”我不理解这句话中加粗部分在编程方面的意义。这段引用来自http://www.openbookproject.net/thinkcs/python/english3e/functions.html《如何像计算机科学家一样思考,第三版》。感谢您!

1
在你的例子中,“抽象”这个词的用法/含义在编程世界内外是相同的。 - Lix
简单来说,如果你在代码中看到一个函数调用,比如calculateFooBar(x, y),你就知道(或者至少有一些想法)这个(可能很复杂的)代码是做什么的,而不需要去阅读和理解实现该函数的所有代码。 - tobias_k
7个回答

157

抽象是计算机科学中的核心概念。没有抽象,我们仍然会编写机器码或者更糟糕的是根本没有计算机。所以在我看来,这是一个非常好的问题。

什么是抽象?

将某物进行抽象意味着给一些事物命名,使得这个名称捕捉到了函数或整个程序所做的核心内容。

你引用的书中提供了一个例子:

假设我们正在与海龟(turtles)一起工作,而我们需要完成的常见操作之一是绘制正方形。“绘制正方形”是一个抽象,或者说是一个“心理块”,它由许多较小的步骤组成。因此,让我们编写一个函数来捕获这个“构建块”的模式:

暂时忘记海龟,想象一下在纸上画一个正方形。如果我告诉你画一个正方形,你马上就知道该怎么做:

  • 画一个正方形 => 画一个所有边长相等的矩形

你可以毫不犹豫地完成这个任务,因为你学过什么是正方形,而不需要我一步步地告诉你。这里,“正方形”这个词就是“画一个所有边长相等的矩形”的抽象。

抽象深入人心

但是,你怎么知道什么是“矩形”呢?这又是根据以下概念的抽象:

  • 矩形 => 画两条互相平行且长度相等的线段,并加上另外两条与这两条线段垂直的平行线段,这两条线段也可能和前面两条长度不同。

当然这个过程还可以继续下去——线段、平行、垂直、连接都是众所周知的概念的抽象。

现在,想象每次要画一个矩形或正方形时,你都必须提供一个完整的定义,或者解释线段,平行线段,垂线和连接线——那会花费太多时间了。

抽象的真正力量

这就是抽象的第一个力量:它们使交流和完成任务变得更容易。

抽象的第二个力量来自于其好的组合性质:一旦你定义了抽象,你可以将两个或多个抽象合并成一个新的、更大的抽象:例如,假设你厌倦了画正方形,但真的想画一座房子。假设我们已经定义了“三角形”,那么我们可以这样定义:

  • 房子 => 画一个正方形在其上方画一个三角形

接下来,你想要一个村庄:

  • 村庄 => 绘制多个房子相邻排列

哦对了,我们还需要一个城市——而且我们有一个新概念街道import nicepic draw_house()

这只需要两行代码,就可以获得更为复杂的东西。这不是很棒吗?


3
你启发了我的概念。 - Dexture
这是一个非常好的解释,可以帮助我们理解抽象概念,但我仍然不明白这与Python类、函数等有什么关系。您能否再详细解释一下? - notilas
@miraculix,如果我理解正确的话,抽象化基本上是封装专用任务(比如建造房屋)的所有实现细节。对于某些人来说,这个任务可能是一个单元/原子/独立的任务(比如对于建造村庄的人),或者是几个更多任务的组合(比如对于知道如何制作三角形、矩形的人)。 - Mandroid
2
@notilas 你可以使用类/函数来抽象一个任务,使用这个答案中给出的例子,你可以有一个名为drawSquare的函数,它将具有绘制正方形的步骤,通过组合抽象,你可以有另一个函数drawHouse,它使用drawSquaredrawTriangledrawHouse不关心drawSquare是如何实现的,它只关心结果。对于drawVillagedrawCity也是一样的。 - Spoody

9
通过抽象类来理解抽象化是一个很好的方式。
假设我们正在编写一个模拟房屋的程序。这个房屋将有几个不同的房间,我们将把它们表示为对象。我们为浴室、厨房、客厅、餐厅等定义一个类。
然而,所有这些都是房间,因此共享几个属性(门/窗户数量、平方英尺等)。但是,房间永远不能单独存在...它总是会成为某种类型的房间。
因此,创建一个名为Room的抽象类是有意义的,其中包含所有房间共享的属性,然后让Kitchen、Living Room等类继承抽象类Room。
房间的概念是抽象的,只存在于我们的头脑中,因为任何实际存在的房间都不仅仅是一个房间;它可能是卧室、客厅或教室。
因此,我们希望我们的代码能够代表我们的“心理分块”。这样做可以使一切变得更加整洁和易于处理。

4
作为计算机科学中的抽象,它试图从常见模式中提取细节,以便程序员可以接近人类思维水平,省略在实践中很重要但对于解决问题并非必要的细节。基本上,它是消除问题的细节。例如,要绘制一个正方形需要几个步骤,但我只想要一个绘制正方形的函数。请参考wikipedia: Abstraction_(computer_science)

2

简单来说,抽象的本质是提取必要的属性,省略不必要的细节。但为什么我们需要省略不必要的细节呢?关键原因是为了防止变化的风险。


2
假设您编写了一个函数,该函数接收一堆文本作为参数,然后在配置文件中读取凭据,使用这些凭据连接到SMTP服务器并发送邮件。该函数应命名为“sendMail(text)”,而不是“parseTextReadCredentialsInFileConnectToSmtpThenSend(text)”,因为这样更容易代表它所做的事情,无论是对自己还是向同事或用户展示API...即使第二个名称更准确,第一个是更好的抽象化。

1

描述某事的最好方法是使用示例:

函数只不过是一系列命令,用于完成任务。基本上,您可以组织一段代码块,执行单个操作。该单个操作可以在整个程序中重复使用多次。

现在,您的函数执行此操作,应将其命名,以便立即识别其功能。一旦命名,您只需通过调用其名称即可在各处重复使用它。

def bark():
  print "woof!"

然后,要使用该函数,您只需执行以下操作:
bark();

如果我们想让它叫4次,会发生什么?那么你可以写四次bark();。
bark();
bark();
bark();
bark();

或者您可以修改函数以接受某种类型的输入,从而改变其工作方式。

def bark(times):
    i=0
    while i < times:
        i = i + 1
        print "woof"

然后我们只需要调用一次:

bark(4);

当我们开始谈论面向对象编程(OOP)时,抽象化指的是一些不同的东西。您稍后会发现这一部分 :)


1

抽象:在硬件和软件中都是非常重要的概念。

重要性:我们人类无法一直记住所有事情。例如,如果你的朋友迅速说出30个随机数字并让你将它们全部相加,你可能做不到。原因?你可能无法记住所有这些数字。即使你将这些数字写在纸上,你也会逐个添加最右边的数字,忽略左边的数字,并在另一次忽略最右边的数字,以添加最右边的数字。

这表明在某个时刻,我们人类可以关注某些特定问题,同时忽略已经解决的问题,将注意力转向未解决的问题。

忽略不重要的事情,集中精力处理最重要的事情(暂时和特定情境下)被称为抽象

以下是程序中如何使用抽象。

下面是世界著名的C语言hello world程序:

//C hello world example hello.c
#include <stdio.h>

int main()
{
  printf("Hello world\n");
  return 0;
}

这是程序员编写的最简单且通常是第一个计算机程序。当您在命令提示符上编译和运行此程序时,输出可能会像这样显示:

enter image description here

以下是重要的问题

  1. 计算机只能理解二进制代码,它如何运行像英语一样的代码呢?你可能会说你使用编译器将代码编译为二进制代码。但是,你是否编写了编译器来使你的程序工作?没有。你在Linux系统上安装了GNU C编译器,只需使用以下命令即可:

gcc -o hello hello.c

它将你的类似英语的C语言代码转换为二进制代码,然后你可以通过以下命令运行该代码:

./hello

因此,编写C程序应用程序时,你不需要知道C编译器如何将C语言代码转换为二进制代码。所以你使用GCC编译器作为抽象层。

  1. 你是否编写过main()和printf()函数的代码?没有。在C语言中,这两个函数已经被其他人定义好了。当我们运行C程序时,它会寻找main()函数作为程序的起点,而printf()函数会将输出打印到计算机屏幕上,它已经在stdio.h中定义了,因此我们需要在程序中包含它。如果这两个函数都没有被预先编写,我们就必须自己编写它们才能打印出两个单词,计算机将成为地球上最无聊的机器。在这里,你再次使用抽象化,即你不需要知道printf如何在监视器上打印文本,你只需要知道如何给printf函数输入,以便它显示所需的输出。

出于简单起见,我没有展开操作系统、内核、固件和硬件的抽象概念。

需要记住的事情:

在编程时,您可以使用各种方式来使用抽象化使程序简单易用。

示例1:您可以使用一个常量来抽象化PI值3.14159,因为PI比3.14159更容易记忆。

例子2:你可以编写一个函数,返回给定数字的平方,然后任何人,包括你自己,都可以通过将其输入作为参数并从中获取返回值来使用该函数。
例子3:在面向对象编程(OOP)中,比如Java,你可以定义一个封装数据和方法的对象,并通过调用它的方法来使用该对象。
例子4:许多应用程序提供API,你可以使用它与该应用程序交互。当你使用API方法时,你永远不需要知道它们是如何实现的。因此,抽象存在于其中。
通过所有这些例子,你可以意识到抽象的重要性以及它在编程中的实现方式。记住一件关键的事情是,抽象是上下文相关的,即根据上下文而变化。

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