为啥 printf() 在 python 中给出一个奇怪的输出?

Posted

技术标签:

【中文标题】为啥 printf() 在 python 中给出一个奇怪的输出?【英文标题】:Why is printf() giving a strange output in python?为什么 printf() 在 python 中给出一个奇怪的输出? 【发布时间】:2017-04-29 19:55:12 【问题描述】:

我尝试在 Linux 的 python 命令行中使用 C 函数 printf()。为了完成这项工作,我导入了ctypes。我的问题是:如果我创建一个 CDLL 的对象以在循环中使用 printf() 函数,我会得到一个非常奇怪的输出:

>>> import ctypes
>>> libc = ctypes.CDLL("libc.so.6")
>>> for i in range(10):
...     libc.printf("%d", i)
...
01
11
21
31
41
51
61
71
81
91
>>>

但是,当我在函数内调用此循环时,它按预期工作:

>>> import ctypes
>>> libc = ctypes.CDLL("libc.so.6")
>>> def pr():
...     for i in range(10):
...         libc.printf("%d", i)
...     libc.printf("\n")
...
>>> pr()
0123456789
>>>

我无法猜测是什么导致了这种行为... 如果重要的话,我会在 Linux 上使用 Python 2.7.6。

编辑:

Python 版本/操作系统对此没有影响。有关详细信息,请参阅下面的 PM 2Ring 的答案。在 Windows 上,您只需将 libc 的初始化更改为 libc = ctypes.CDLL("msvcrt.dll"),其中 .dll 是可选的。除了调用函数之外,获得正确输出的另一种方法是将printf() 的返回值存储在变量中:

>>> import ctypes
>>> libc = ctypes.CDLL("libc.so.6")  # "mscvrt.dll" on windows
>>> for i in range(10):
...     r = libc.printf("%d", i)
...
0123456789>>>

我还是更喜欢这个函数,因为你可以更轻松地添加结束换行符。

【问题讨论】:

实际上抑制输出的更简单方法是在末尾添加一个分号:libc.printf("%d", i); 【参考方案1】:

每个数字末尾的那些额外的'1's 是来自printf 的返回值,它返回它打印的字符数。交互式解释器中调用的函数的返回值会自动打印出来(除非它是None)。

事实上,交互式解释器会打印任何未分配的非None 表达式。当然,它会为这些表达式添加一个换行符,这解释了为什么您的第一个代码块中的输出位于不同的行中。

您的pr 函数没有返回语句,因此它返回None,因此不会打印额外的内容。

【讨论】:

并且它没有在函数中打印出来,因为函数代码不再是交互式的了? @Aemyl 基本上。您在交互式上下文中调用pr,因此它的返回值由解释器打印,但pr 调用的任何函数都可以正常工作,否则终端将充满不需要的垃圾。 :) @Aemyl 不,这是标准(CPython)解释器的所有版本和所有操作系统的标准。 还有其他 Python 解释器/IDE,但默认情况下,它们也会在以交互模式 (AFAIK) 运行时打印非None 表达式的值,尽管它们也可能会打印其他信息。 没有 printf 调用也一样:for i in range(10): 1 注意:ipython(一个非常常见的替代交互式解释器,它包装了 CPython 解释器,作为众多功能之一)更智能,并且不输出结果块中未分配的表达式。它仅输出未分配的单个表达式。【参考方案2】:

虽然 PM 2Ring 已经很好地回答了这个问题,但我认为值得指出 printf 的行为或非常接近的行为可以作为 python 内置使用,所以你使用 C 真的很奇怪库版本,除非您使用它来学习如何使用ctypes,在这种情况下继续。

但另一方面,当你没有使用足够多的 python 来了解 REPL 的工作原理时,想要使用 ctypes 是很奇怪的...冒着听起来傲慢的风险,作为一个拥有 10 年 python 经验的人,我实际上从未使用过ctypes

python中有两个内置的字符串格式化选项:

print("%d" % (i,))

这是旧样式,与printf非常相似,并且

print(":d".format(i))

这是 python 3 的新功能,并且功能更强大。有questions 解决此站点上的差异。上述两行的输出都与libc.printf("%d\n", i) 相同。也可以print without the newline。

“%”字符串格式化的 CPython 实现实际上使用了 sprintf under the hood。

【讨论】:

我同意在 Python 中使用 C 语言的 printf 几乎没有任何意义,除了作为使用 ctypes 的学习练习。我想它返回打印的字符数 可能 很方便,但使用普通的 Python 字符串格式并在打印之前获取结果的len 会更明智。 FWIW,format 函数和方法已被向后移植到 Python 2.6 和 2.7。 Python 3 引入了另一种格式化字符串的方法:Literal String Interpolation aka f-strings。 FWIW,我也没有经常使用ctypes,但here 是我编写的一个程序,它使用它来访问 OpenSSL 加密库以实现可恢复的 SHA256 哈希函数。

以上是关于为啥 printf() 在 python 中给出一个奇怪的输出?的主要内容,如果未能解决你的问题,请参考以下文章

python - 为啥在python中使用int()将二进制转换为整数会在将2作为基本参数时给出错误

为啥解包在 Python 中给出一个列表而不是一个元组?

为啥这个 sizeof(c+a) 给出 4 字节而不是 3

为啥选择排序算法的这段代码没有给出输出?

第一次提问:为啥'py'被识别但'python'不被识别?在终端中运行的快捷方式? 'echo %PATH%' 给出巨大的路径?

为啥 ++(*p) 没有给出 l-value required 错误?