数据科学家的内存 6 个 CS50 课程以避免错误

数据科学家容易忽略的内存问题

本书旨在指导数据分析师和科学家通过了解内存的真正工作原理来编写更健壮、高效和可预测的代码。

引言:逻辑之外的漏洞

你有一个 Python 脚本在少量数据样本上运行完美,但在处理其他数据时崩溃并出现神秘错误。 MemoryError on the full dataset. Or maybe you’ve encountered a bug in a data pipeline that vanishes when you try to debug it step-by-step. These frustrating problems often have nothing to do with a flaw in your business logic and everything to do with a gap in understanding what the computer is doing “under the hood.”

许多这类高层次、难以预测的问题都源于计算机科学中关于内存的基本概念。理解这些概念不仅仅是学术探讨,更是编写更优质、更可靠代码的实用技能。本文基于 CS50x 2025(哈佛大学)关于内存的讲座内容整理而成,该讲座视频可在 YouTube 上观看。

数据科学家的内存

要点一:“弦”是一种错觉

在许多介绍性语境中, string is presented as a basic data type. The CS50 library, for instance, uses it as a helpful “training wheel” to get students started. However, in a foundational language like C, there is no built-in string data type. It’s an abstraction.

我们称之为字符串的东西实际上是 char *—a pointer to a character. This pointer is simply the memory address of the 第一 序列中的一个字符。为了知道字符串在哪里结束,C 语言依赖于一个特殊的哨兵值:空终止符(\0), a byte containing all zeros. Functions like printf read from the starting address character by character until they hit this null terminator. Without it, the function would just keep reading into adjacent memory, causing unpredictable behavior.

数据科学连接: 这个概念是高级语言如Python和的祖先 R 等高级语言处理字符串和其他可变长度数据的雏形。它解释了为什么字符串操作的计算成本很高;系统通常需要扫描终止点或管理底层数组。对于数据科学家而言,这在解析原始文本文件或使用 API 时至关重要;缺失或错误的终止字节会导致数据损坏或解析失败,例如日志文件行读取错误并截断重要的错误信息。

要点二:为什么 == Can Lie About Equality

在 C 语言中比较两个整数变量时 ==, you’re comparing their values. But if you compare two string variables with ==, you are not comparing their content. Instead, you are comparing their memory 地址.

如果你有两个相同的字符串“high”和“high”,但它们存储在内存中的不同位置,那么 == operator will return false. Their content is the same, but their addresses are different. This is why C requires a special function like strcmp (string comparison), which performs a character-by-character check to determine if the strings’ values are truly identical.

数据科学连接: 这与Python中对象同一性与值相等性的概念直接对应。 is in Python compares memory addresses (identity), while == typically compares values (equality). For complex objects like Pandas DataFrames, even == can be ambiguous. This is why dedicated methods like pandas.DataFrame.equals() exist. The fundamental C behavior explains why you can’t just compare two DataFrames with a simple operator; you need a robust method that iterates through and compares their actual contents, just like strcmp does for C strings.

要点三:复制品可能只是别名

在 C 语言中,类似这样的指令 string t = s; might look like it’s creating a new copy of string s and naming it t. It is not. This operation does not copy the string’s content (the characters ‘H’, ‘I’, ‘!’, ‘\0’). It only copies the 地址 字符串的第一个字符。

这意味着两个变量, s 和 t, now point to the exact same block of memory. They are two different names for the same underlying data. Consequently, if you modify the string using the variable t (for example, by capitalizing the first letter), you are also changing the string for s. There is only one string, and both variables point to it.

数据科学连接: 这就是 Pandas 用户经常困惑的“视图 vs. 副本”问题的根源。数据科学家可能会过滤一个 DataFrame,将其赋值给一个新变量,然后开始进行修改,并认为自己正在处理一个独立的副本。但如果 Pandas 返回的是一个“视图”(指向原始数据的别名),那么他们实际上是在无意中修改了源数据集。这可能会导致一些难以追踪的隐蔽而危险的错误,因为原始数据会被其他地方执行的操作“神秘地”修改。

“当你直接使用 `equals` 来比较两个字符串时……也就是比较两个字符星号,也就是比较两个地址,显然它们位于不同的位置……我们需要一个解决方案,这就是我们引入 `stir comp` 函数的原因,它能够正确地解决这个问题。” — 哈佛 CS50x 2025(YouTube)— 讲座:内存

要点四:内存是一种有限的资源,你必须合理管理。

当程序运行时需要内存时——例如,存储从文件中读取的数据——它可以向操作系统请求内存。在 C 语言中,这是通过一个名为 `match` 的函数来实现的。 malloc() (memory allocation), which asks for a specific number of bytes from a large memory pool called the “heap.”

但是,此请求可能会失败。如果系统内存不足, malloc() will return NULL (a special pointer to address zero) to signal failure. Robust code must always check for this possibility. Furthermore, once the program is finished with that memory, it must explicitly return it to the system using free(). Failing to do so creates a “memory leak,” where the program holds onto memory it no longer needs, potentially causing it to run out of resources and crash over time.

数据科学连接: 这直接适用于处理大型数据集。尝试将大型 CSV 或 Parquet 文件加载到 Pandas DataFrame 中,如果脚本请求的内存超过系统可用内存,很容易导致脚本崩溃。 malloc 和 free are fundamental to writing scalable data pipelines, understanding why certain processes are “memory-bound,” and appreciating the design of modern data tools that use techniques like chunking or memory mapping to process data that won’t fit into memory all at once.

要点五:函数拥有私有工作空间(栈)

当一个函数被调用时,它会在称为“栈”的内存区域中获得自己的私有工作空间。这个“栈帧”保存着它的局部变量,包括传递给它的参数。

CS50 课程通过以下方式演示了这一点: swap function intended to swap two integer values. The initial version fails because it only swaps 副本 函数在其自身的私有栈帧内传递值。这被称为“按值传递”。函数执行完毕后,其栈帧消失,调用函数中的原始变量(main) are left completely unchanged. The corrected version works by “pass by reference”—passing the memory 地址 原始变量。这给出了 swap function the power to reach back into the caller’s stack frame and modify the original variables directly.

数据科学连接: 这个概念对于理解编写数据转换函数时的变量作用域和可变性至关重要。它有助于解释为什么函数可能无法按预期修改传递给它的 DataFrame(例如,如果它在内部重新赋值变量而不是就地修改对象)。它还强调了“纯函数”(不修改其输入的函数)的设计原则,这对于创建可复现、可测试且无错误的分析流程至关重要。

要点六:价值十亿美元的漏洞是一个差一错误

当程序试图将数据写入超出内存缓冲区(例如数组)已分配边界的范围时,就会发生“缓冲区溢出”。这就像试图将一加仑水倒入一个品脱杯中——多余的水必须流向某个地方,而且通常会覆盖相邻的内存,导致数据损坏和程序崩溃。

本次讲座引用了一起涉及 CrowdStrike 安全软件的真实事件。一次更新中的漏洞导致全球系统崩溃。根本原因出奇地简单:代码预期数组包含 20 个值,但实际接收到的是 21 个值。尝试访问不存在的第 21 个元素导致了“越界内存读取”,最终造成系统崩溃。C 语言没有提供针对此类漏洞的自动保护机制,因此这有力地展示了一类危险漏洞的危害。

数据科学连接: 这是数据处理中防御性编程的终极教训。在解析文件、遍历列表或处理 API 响应时,您不能保证传入的数据完全符合预期。这种底层漏洞凸显了稳健的错误处理和输入验证的重要性。在处理数据之前,务必显式地检查预期格式、大小和边界,以防止整个流程因一个意外值而崩溃。

今天应该申请什么:一份实用清单

  • 审视你的平等性检验: 在比较模型或数据框等复杂对象时,请暂停并确认是否需要了解它们是否相同。 同一个物体 在记忆中(身份)或者如果他们只是 包含相同的数据 (相等)。使用适当的方法(is 与。 .equals()) for your goal.
  • 审核您的数据框修改: 在修改你认为是 DataFrame 副本的内容之前,请务必先检查它是视图还是副本。对视图的简单更改可能会悄无声息地损坏你的原始数据集。 .copy() to ensure you have an independent object.
  • 主动提升记忆力: 处理大型文件时,不要直接加载然后祈祷一切顺利。使用工具检查可用系统内存,并将文件分成较小的块进行处理(例如,使用 chunksize 在 pandas.read_csv) to avoid requesting more memory than is available.
  • 验证数据边界: 当您通过索引访问数据时(例如在列表或数组中),务必将其视为潜在的故障点。添加检查以确保索引在预期范围内,尤其是在数据大小由外部数据源决定的情况下。
  • 永远不要相信输入数据: 将来自文件、API 或用户输入的数据视为本质上不可靠的数据。在执行操作之前,添加断言或显式检查数据类型、大小和格式,以便在意外问题影响分析之前将其捕获。

9. 结束语

这些底层内存概念并非只是学术上的琐碎知识;它们直接体现在我们高级数据科学工作中遇到的各种问题,例如程序错误、性能瓶颈和安全风险。通过理解内存中的运作机制,我们可以从被表象迷惑转向诊断根本原因。随着 CS50 课程的深入,这些原理将成为构建高效数据结构的基石,而这些数据结构正是我们数据库和算法的驱动力。

你代码中最近出现的哪些“奇怪”的错误现在可以用这些核心内存原理之一来解释?

类似的帖子