如何刷新 Python print 函数的输出2024年8月29日 | 阅读 10 分钟 在本教程中,我们将学习如何使用 print() 函数的 flush 参数显式刷新输出数据缓冲区。我们还将确定何时需要刷新数据缓冲区以及何时不需要刷新。我们还将讨论如何更改单个函数和脚本的数据缓冲。 print() 函数在默认参数下的行为取决于我们是在交互模式还是非交互模式下运行 Python。当以交互方式运行 Python(例如,在 Jupyter Notebook 中)时,print() 的输出是行缓冲的,这意味着每行输出一经生成就会立即写入屏幕。但是,当以非交互方式运行 Python(例如,从命令行运行 Python 脚本)时,输出是块缓冲的,这意味着它会存储在缓冲区中,直到累积到一定量的数据,然后一次性写入屏幕。 行缓冲意味着输出会在每个换行符 \n 打印后刷新(即写入屏幕)。块缓冲意味着输出会存储在缓冲区中,直到累积到一定量的数据,然后一次性刷新。 Python 如何缓冲输出当我们对文件类对象进行写入调用时,Python 默认会缓冲调用——这是一个好主意!与随机存取存储器 (RAM) 访问相比,磁盘写入和读取操作很慢。当脚本通过将字符批量存储在 RAM 数据缓冲区中,然后通过单个系统调用将它们一次性写入磁盘来减少写入操作的系统调用次数时,我们可以节省大量时间。让我们以一个真实的例子来理解缓冲—— 交通灯可以作为缓冲的一个例子;如果每辆车一到达路口就被允许通过,就会导致交通堵塞,来自不同方向的汽车会卡在路口。相比之下,交通灯会缓冲一个方向的交通流量,同时允许另一个方向的交通移动,从而防止交通堵塞并确保整体交通流量更有效。同样,在将数据写入磁盘之前将其缓冲到内存中,可以更有效地利用系统资源,减少写入数据所需的系统调用次数,并提高系统的整体性能。 有时,我们希望在数据缓冲区刷新之前将其填满。例如——救护车需要尽快通过,我们不希望它在交通灯处等待,直到排队一定数量的车辆。 在开发程序时,通常需要获取代码执行的实时反馈。在这种情况下,立即刷新数据缓冲区至关重要。以下是立即刷新有益的几个场景示例:
在上述两种情况下,都需要在输出生成后立即读取,而不是等待足够的输出累积在缓冲区中并将其刷新。否则可能导致数据丢失或过时,从而在程序中引发错误或其他问题。 在许多情况下,缓冲是有益的,但在某些时候,过多的缓冲会导致缺点。我们可以根据需要实施不同类型的数据缓冲。
在 Python 中,当我们写入文件类对象时,会使用块缓冲。但是,如果我们在交互式环境中写入,它会执行行缓冲。 让我们看下面的脚本以更好地理解。 示例 - 输出 3 2 1 Go! time 模块中的 sleep() 函数用于暂停程序执行指定的秒数。在此示例中,它会在每次计数之间暂停程序 1 秒。 range() 函数用于创建从 3 到 1(不包括 0)的数字序列,步长为 -1(即倒计时)。 如果程序缓冲整个倒计时并在完成后才打印,可能会导致在起跑线等待的运动员感到困惑和不确定。因此,有必要在每次倒计时发生时打印出来,并在一秒钟的延迟,这样运动员就能清楚准确地知道倒计时何时接近尾声。 为 Python 刷新打印输出添加换行符如果我们在 Python REPL 中运行代码片段,或者直接使用 Python 解释器将其作为脚本执行,我们不会遇到任何问题。在交互式环境(例如终端)中运行程序时,标准输出流 (STDOUT) 将是行缓冲的。这意味着当你使用 print() 函数向 STDOUT 写入内容时,输出将保留在缓冲区中,直到遇到换行符 (\n)、缓冲区变满或程序结束。 当遇到换行符时,缓冲区会自动刷新,这意味着缓冲区的所有内容会立即写入 STDOUT。这就是为什么在使用 print() 输出多行文本时,每行一经打印就会立即出现,而无需等待缓冲区填满或程序结束。 如果我们使用 print() 及其参数编写上述脚本,则每次调用 print() 的末尾都会隐式写入一个换行符。 示例 - 输出 3 2 1 Go 当我们使用 print() 函数输出数字时,数字会连同换行符 (\n) 一起发送到输出缓冲区。由于我们使用的是交互式环境(例如终端),print() 函数以行缓冲模式运行,这意味着在每次 print() 调用之后,缓冲区会自动刷新并将输出显示在终端上。因此,每个数字都会单独发送到终端,并带有自己的系统调用,从而导致数字立即显示在终端上。 为了在输出中获得换行符,最好避免向 end 传递任何参数。在这种情况下,print() 语句将对 end 参数使用默认值 "/n"。 在下一节中,我们将讨论一个可能发生的数据缓冲意外问题,如果我们出于性能原因修改代码。此外,我们将发现如何解决此问题以确保我们的程序按预期运行。 设置 Flush 参数我们知道 print() 有一个 end 参数,并且想将倒计时数字打印在一行中。我们可以将 end 值从默认换行符更改为空格 ("")。让我们理解下面的例子。 示例 - 输出 3 2 1 Go! 尽管我们打算在一行中打印数字,但为了性能而重构代码导致了一个新问题。数字不是在倒计时时一个接一个地出现,而是在程序执行完毕后同时出现。 块缓冲是导致意外行为的原因。尽管 print() 写入的输出流仍然是终端(一个交互式环境),但由于修改了 end 参数,行缓冲的行为发生了变化。输出流在技术上仍然是行缓冲的,和以前一样,但由于 end 参数已更改,不再写入任何换行符 (\n)。因此,行缓冲从未被触发。 示例 - 输出 3 2 1 Go 我们将默认的 flush 值更改为 True,这将刷新输出流,而与我们正在写入的文件流的默认数据缓冲无关。 如果我们决定从 print() 语句中删除任何换行符,需要注意的是,Python 的默认行为是缓冲输出而不是实时显示。输出只会在缓冲区满或程序执行完毕后才会显示。为了确保您的输出实时显示,我们必须将 flush 参数设置为 True。通过这样做,Python 将在每个 print() 语句之后立即刷新输出缓冲区,允许输出在终端上实时显示。 更改 PYTHONBUFFERED 环境变量要在不更改现有代码的情况下更改 PYTHONBUFFERED 环境,我们需要在没有数据缓冲区的情况下使用命令执行脚本。 示例 - 根据操作系统,我们将使用 cat 或 echo 命令来监视脚本。要在不更改源代码的情况下无缓冲地运行 countdown.py,我们可以在将其输出传输到 cat 或 echo 之前使用 -u 命令选项执行 Python。 -u 命令选项用于禁用输出流、标准输出 (stdout) 和标准错误 (stderr) 的数据缓冲区。 即使将程序的输出传输到另一个程序,我们也可以从 print() 中获得无缓冲输出,而无需对代码进行任何更改。在此方法中,print() 直接写入标准输出,而无需缓冲数据,从而允许输出立即传输到管道中的下一个程序。 除了依赖 -u 命令行选项来实现无缓冲输出之外,您还可以显式地将 PYTHONUNBUFFERED 环境变量设置为非空字符串。默认情况下,此变量设置为空字符串,但将其设置为任何其他值将导致 Python 在当前环境中的所有脚本运行中以无缓冲模式执行。这提供了一种替代方法,可以确保您的程序输出立即显示,无论是否将其传输到另一个程序。 您需要在运行脚本之前更改环境中的 PYTHONUNBUFFERED 值,此更改才会生效。 对于 Windows - 对于 Linux - 使用 functools.partial 更改 Print 的签名在本节中,我们将探讨另一个实际情况。假设一个团队引入了一个大型数据库,并要求对现有日志文件进行自动化监控。 我们已将脚本任务分配给使用 print() 监视,以将日志信息发送到输出流。 由于输出缓冲,脚本的输出无法实时监控。该脚本包含大量的 print() 语句,使其难以修改而不影响原始代码库。为了最大限度地减少对原始脚本的更改,找到不需要大量修改的解决方案非常重要。 更改其函数签名是快速修改我们要监视的脚本中 print() 默认行为的一种方法。这可以通过创建 print() 函数的修改版本来实现,该版本包含所需的默认行为,而无需修改原始脚本。 示例 - 当我们在脚本顶部添加上述两行代码时,我们将 print() 的内置签名更改为将 flush 设置为 True。通过在脚本中创建 print() 函数的修改版本,所有后续对 print() 的调用都将自动使用更新后的函数签名。即使输出流不是终端,数据缓冲区也会在每次 print() 调用后刷新。这种修改有助于确保无论输出流的目的地如何,都可以实时监控脚本的输出。 让我们理解下面的例子。 示例 - 输出 3 2 1 Go! 这种方法允许您在脚本中使用缓冲和非缓冲 print() 调用。通过预先定义一个指定非缓冲写入标准输出的函数,您可以轻松地将其集成到现有代码库中,而无需修改每个 print() 调用。此外,给函数一个描述性名称可以帮助其他开发人员比在每个 print() 调用中添加 flush=True 参数更快地理解修改的目的。 结论本教程包括使用 print() 的 flush 参数显式刷新输出数据缓冲区。我们探讨了更改单个函数、整个脚本甚至整个 Python 环境的数据缓冲。通过运行一个稍作修改的短代码片段,您可以观察到 print() 在交互模式下以行缓冲方式执行,否则以块缓冲方式执行。您还了解了何时可能需要更改此默认行为以及可用的方法。 |
在本教程中,我们将学习如何在 Python 中获取亲和数。首先,我们将了解亲和数是什么以及如何使用它们。亲和数是两个不同的数字,它们之间存在如此的关系,即每个数字的真因子之和都等于...
阅读 3 分钟
'and' 运算符是 Python 中的一个逻辑运算符,用于组合两个或多个条件。此操作返回一个布尔值 - 如果所有条件都为真,则返回 True;否则,返回 False。Python 'and' 运算符的语法如下:语法:condition_1 and condition_2 如果两者都...
5 分钟阅读
在 CPU 中,调度方法选择进程的执行顺序,从而管理等待时间。其中一种方法被称为“最短作业优先”(SJF)或“最短作业”。该算法将最短的执行时间赋予进程...
5 分钟阅读
在本教程中,我们将学习如何使用 Matplotlib 在子图中包含图例。创建图后可以使用 legend() 函数添加图例。语法:子图中图例的语法是:axes[position].legend(loc = ''),其中 loc 用于位置。方法:以下是我们将介绍的方法...
阅读 3 分钟
简介:在本文中,我们将讨论 Python 描述符。描述符旨在管理许多通过引用获取项的训练的属性。描述符使用了三种不同的策略:__getters__()、__setter__() 和 __delete__()。当这种方法在对象上定义时,我们将调用...
阅读 3 分钟
在本教程中,我们将学习如何将 tqdm 与 pandas 库一起实现。tqdm 模块用于根据需要创建进程条。进程条是估算和显示任务所需时间的重要工具。要理解...
5 分钟阅读
Python的一个关键特性是“类型转换”。它允许程序员将变量或数据从一种数据类型转换为另一种数据类型。它可以实现数据流畅处理。在处理大量数据时,数据以……形式存在时,它会非常有用。
7 分钟阅读
Python 提供了一个强大的模块 collections,它包含许多有用的数据结构,除了列表和字典等内置类型之外。collections 中一个特别有用的模块是 collections.abc,它提供了一组用于集合的抽象基类。在本文中,我们将探讨...
5 分钟阅读
学习:一步一步的方法 Python 语言的基本概念对于程序员和网络工程师至关重要。谁是网络工程师,他们做什么?网络工程师计划、执行和监督网络。此外,由于网络复杂多变,他们还会进行检查...
阅读 6 分钟
Python 中的 "isna()" 函数 isna() 方法在 Python 中是一个强大的数据操作和分析工具箱,在处理 pandas 时被广泛使用。isna() 函数用于查找 pandas DataFrame 或 Series 中的缺失或空值。isna() 函数在各种场景中的使用...
阅读 3 分钟
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India