猿问

打印管道是否可以在引擎盖下连接到不同的流?

在运行以下脚本时,我遇到了一些奇怪的行为。


如您所见,好像write被多次调用了,我不知道为什么会这样,因为我已经明确覆盖了file=sys.stdout行为。


引擎盖下的打印管道流如何精确地输送到所有通道?它是否具有某些默认行为,除了以下内容外,文档不是很具体:


file参数必须是带有write(string)方法的对象;如果不存在或无,则将使用sys.stdout。


测试脚本


import sys


def debug(*args, **kwargs):

    pass


def _debugwrite(obj):

    print("You're looking at Attila, the psychopathic killer, the caterpillar")

    out = sys.stderr

    out.write(obj)


debug.write = _debugwrite


print("Don't you ever disrespect the caterpillar", file=debug)

输出:


You're looking at Attila, the psychopathic killer, the caterpillar

You're looking at Attila, the psychopathic killer, the caterpillar

Don't you ever disrespect the caterpillar

我所期望的:


You're looking at Attila, the psychopathic killer, the caterpillar

Don't you ever disrespect the caterpillar

我试了一下:


我试图使用inspect模块来获取调用方,也许看到实际的调用是谁写的,但是我得到了moduleidk为什么:(这很明显吗?


进一步的问题:


有什么方法可以调试超出函数范围Python并进入基础C调用的方法?因为主Python分布很好,所以CPython,如果我的理解是正确的,Python则它只是api基础C代码的一个。通话最终Python会转换为C后台通话。因此,举例来说,我发现print C在C中定义如下,但是对我来说很难理解那里发生了什么(因为erm,我不知道C),但是也许通过调试器我可以打印出一些东西,看看是什么,并弄清楚至少是流程,如果不是全部的话。我非常想了解引擎盖下的总体情况,而不是理所当然地认为。


吃鸡游戏
浏览 129回答 2
2回答

宝慕林4294392

当答案非常简单时,您正在寻找的是非常复杂的东西。我什至不知道“通向所有渠道”是print什么意思,但是什么也不做。它所做的只是调用传递给它write的file对象。然而,它要求write每进行一次的说法,每进行一次sep,并一度为end。因此,此行:print("Don't you ever disrespect the caterpillar", file=debug)…大致相当于:debug.write(str("Don't you ever disrespect the caterpillar"))debug.write("\n")……这当然意味着您print两次收到额外的消息。顺便说一句,将来用于调试或理解这样的事情:如果您将多余的内容更改print为包括repr(obj),那么将是显而易见的:def _debugwrite(obj):&nbsp; &nbsp; print("stderring " + repr(obj))&nbsp; &nbsp; out = sys.stderr&nbsp; &nbsp; out.write(obj)输出为:stderring "Don't you ever disrespect the caterpillar"stderring '\n'Don't you ever disrespect the caterpillar不再是很神秘了吧?当然,stdout和stderr是独立的流,具有自己的缓冲区。(默认情况下,与TTY通话时,它stdout是行缓冲的,并且stderr是非缓冲的。)因此,排序不是您天真想要的,但这是有道理的。如果仅添加flushes,则输出将变为:stderring "Don't you ever disrespect the caterpillar"Don't you ever disrespect the caterpillarstderring '\n'(末尾有空白行)。对于您的奖金问题:我试图使用检查模块来获取调用者,也许看到谁在写实际的调用,但是我得到了模块,idk为什么:(这很明显吗?我假设你做了类似的事情inspect.stack()[1].function?如果是这样,则您要检查的代码是模块中的顶级代码,因此将其inspect显示为名为的伪函数<module>。有什么方法可以调试Python以外的函数并进入基础C调用?当然。只需在lldb,gdb,Microsoft的调试器或通常用于调试二进制程序的任何其他程序下运行CPython本身即可。您可以将断点放置在ceval循环中或特定的C API函数中,也可以放置在任意位置。您可能需要构建CPython的调试版本(请./configure --help参阅选项),以使其变得更好。因为主要的Python发行版是CPython,如果我的理解是正确的,Python只是底层C代码的api。好吧,不完全是。它是一个编译器和一个字节码解释器。该字节码解释器很大程度上使用与扩展/嵌入接口公开的相同的C API,但是重叠并不是100%;在某些地方它处理C API级别以下的结构。Python中的调用最终在后台转换为C调用。因此,例如,我发现打印在C中的定义如下,但是对我来说很难理解那里发生了什么(因为erm,我不了解C),但是也许可以通过调试器来打印东西弄清楚是什么,如果不是全部的话,至少要弄清楚流程。我非常想了解引擎盖下的总体情况,而不是理所当然地认为。是的,您可以这样做,但是您将需要同时理解C和CPython API(例如,如何查找与等价的C插槽之类的东西__call__),以找出放置断点并开始跟踪的位置。对于这种情况,只用Python编写包装器并在Python中调试它们就容易得多。例如:import builtinsdef print(*args, **kwargs):&nbsp; &nbsp; return builtins.print(*args, **kwargs)或者,如果您担心print在其他模块中被调用,而不仅仅是在您的模块中被调用,您甚至可以将其隐藏在builtins:builtins._print = builtins.printdef print(*args, **kwargs):&nbsp; &nbsp; return builtins._print(*args, **kwargs)builtins.print = print现在,您可以在Python级别使用pdb中断每个调用print,而不必担心C。当然,您甚至可以在PyPy或Jython或其他任何工具中调试此代码,以查看它与“内置”级别以上的CPython是否有任何不同。
随时随地看视频慕课网APP

相关分类

Python
我要回答