Menu
CoddyTech

编程中的抽象是什么?

在编程中,抽象(abstraction)指把某样东西的实现细节藏在一个更简单的接口后面,让你只需知道它做什么就能使用它。调用 sorted(names) 就能给列表排序,你不必编写排序算法,甚至看都不用看一眼。

作者 Kevin Spektor, 联合创始人兼 CTO

更新于 2026年9月24日

当你写下 print("hi") 时,你不会去想这几个字母是怎么出现在屏幕上的。在这一行代码之下,Python 先把文本转换成字节,再请求操作系统把它们写出去;操作系统把字节交给终端,终端再用字体把每个字母绘制成像素。每一层都向上一层提供一个简单的承诺,同时隐藏自己是如何兑现这个承诺的。这种隐藏就是抽象,而每个程序都建立在许多这样的层之上。

抽象的层次

层次你操作的对象它隐藏了什么
你的程序print("hi")、sorted(names)库是如何完成工作的
Python 及其标准库字符串、列表、文件内存管理、系统调用
解释器(CPython)字节码运行字节码的 C 代码和机器码
操作系统文件、进程磁盘块、设备驱动、CPU 调度
硬件机器指令晶体管和电信号

每一层都由下一层构建而成,但不了解下一层也能理解它。你不知道磁盘块是什么,照样能写出能运行的 Python 代码;操作系统也不需要知道文件里装的是 Python 代码,照样能把它存好。

代码中的抽象是如何实现的

程序员主要用两种方式构建自己的抽象层。

过程抽象就是函数。函数名、参数和返回值构成接口,函数体则是被隐藏的细节。一旦有了 average(scores),程序的其他部分直接使用它即可,不必去看它是怎么相加和相除的。

数据抽象根据“能对一个类型做什么”来定义它,而不是根据它如何存储数据。队列就是“从队尾加入,从队首取出”。至于元素在内存中如何排列,则是内部自己的事:

serving soup
serving pasta
serving cake

底部的代码对 deque 一无所知。如果你把它换成普通列表加 pop(0),输出完全一样,只是队列很长时会更慢,因为删除列表的第一个元素需要移动其余所有元素。接口没有变,所以类外面的代码一行都不用改。

抽象类与接口

在面向对象编程中,抽象通常指规定一组类必须做什么,而把怎么做交给每个类自己决定。在 Python 中,abc 模块可以把方法标记为抽象方法;只要类中还有未实现的抽象方法,就不能实例化:

email to ana@example.com: Your package arrives today
sms to +15550100: Your package arrives today
Notifier is abstract: create an Email or an SMS instead

remind() 只依赖于抽象:任何带有 send 方法的东西都行。要增加推送通知,只需再写一个新类,remind() 完全不用动。最后这一步,即同一个调用对不同的类执行不同的代码,就是多态:抽象定义共同的接口,多态让它真正运转起来。

Java 用关键字提供了同样的工具:

interface Notifier {
    void send(String to, String text);
}

class Email implements Notifier {
    public void send(String to, String text) { /* talk to the mail server */ }
}

写 new Notifier() 会导致编译错误,因为接口没有可以运行的实现。文档详细讲解了 Java 中的抽象类和接口。

抽象与封装的区别

这两个概念经常一起出现,所以很容易混淆。

抽象封装
回答的问题它能做什么?谁可以访问数据?
关注点接口:对外提供的操作内部:接口背后的数据
主要工具函数、抽象类、接口、模块private、_name、属性(property)、#fields
示例Notifier.send() 同时适用于邮件和短信_balance 只能通过 withdraw() 修改
层面设计实现

抽象决定展示哪些操作;封装确保其余部分保持隐藏。上面的 Queue 两者兼具:add 和 next 是抽象,_items 前面的下划线则是封装。

有漏洞的抽象和其他常见错误

Joel Spolsky 在 2002 年提出了抽象泄漏定律:任何不简单的抽象,都会让一部分被隐藏的细节漏出来。浮点数是最经典的例子。它们看起来就像普通的小数,直到底层的二进制存储方式暴露出来:

0.30000000000000004
False

0.1 和 0.2 都无法用二进制位精确存储,所以两者之和会有一点偏差。你可以用好几年浮点数都不必考虑这个问题,但不可能永远不碰到它。这也是为什么了解你所在层次的下一层会很有帮助。

另外两个错误也很常见:

  • 过早抽象。 为永远不会出现的情况搭建的类层次结构,只会变成额外需要阅读和修改的代码。Sandi Metz 说过:“重复的代价远比错误的抽象低。”
  • 把抽象和 abstract 关键字混为一谈。 抽象类只是工具之一。每个命名得当的函数也都是一种抽象。

下一步学什么

阅读封装和多态,补全你对面向对象的整体认识。操作系统页面介绍了你的程序所依赖的最大的抽象。想要动手练习,Python 课程从一开始就带你编写类和函数,Java 课程则讲解了抽象类和接口。

常见问题

抽象有哪些例子?
函数就是最简单的例子:你调用 len(items) 时知道它返回什么,却不需要知道 Python 是怎么数的。汽车踏板是生活中的版本,你不必了解发动机如何燃烧燃料,也能控制车速。在面向对象的代码里,像 Notifier 这样只有一个 send 方法的接口,就是对邮件、短信和推送消息的抽象。
Python 中的抽象是什么?
Python 里的每个函数、类和模块,都是对其内部代码的抽象。如果想要更正式的写法,可以用标准库的 abc 模块配合 @abstractmethod 声明抽象基类;之后 Python 会拒绝为这个类创建对象,也会拒绝为任何没有实现全部抽象方法的子类创建对象。
Java 编程中的抽象是什么?
Java 通过抽象类和接口来表达抽象。抽象类可以包含抽象方法,这类方法没有方法体,必须由子类实现,而且抽象类不能用 new 实例化。接口列出实现它的类必须提供的方法,一个类可以同时实现多个接口。
面向对象的四大特性是什么?

封装、抽象、继承和多态。封装保护对象的数据,抽象只暴露对象能做什么,继承让一个类复用另一个类的代码,多态让同一段代码通过共同的接口处理多种类型。

抽象会让代码变得更糟吗?

会,如果抽象来得太早或者藏错了东西。为永远不会出现的情况而设计的代码,会多出好几层需要阅读和修改的结构,却带不来任何好处。一个常见的经验是:等你把相似的代码写过两三遍,共同的结构已经清晰了,再给它命名。

Coddy 编程语言插图

和 Coddy 一起学编程

开始