为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

吃鸡游戏 2021-03-29 17:14:58
在运行以下脚本时,我遇到了一些奇怪的行为。如您所见,好像write被多次调用了,我不知道为什么会这样,因为我已经明确覆盖了file=sys.stdout行为。引擎盖下的打印管道流如何精确地输送到所有通道?它是否具有某些默认行为,除了以下内容外,文档不是很具体:file参数必须是带有write(string)方法的对象;如果不存在或无,则将使用sys.stdout。测试脚本import sysdef debug(*args, **kwargs):    passdef _debugwrite(obj):    print("You're looking at Attila, the psychopathic killer, the caterpillar")    out = sys.stderr    out.write(obj)debug.write = _debugwriteprint("Don't you ever disrespect the caterpillar", file=debug)输出:You're looking at Attila, the psychopathic killer, the caterpillarYou're looking at Attila, the psychopathic killer, the caterpillarDon't you ever disrespect the caterpillar我所期望的:You're looking at Attila, the psychopathic killer, the caterpillarDon't you ever disrespect the caterpillar我试了一下:我试图使用inspect模块来获取调用方,也许看到实际的调用是谁写的,但是我得到了moduleidk为什么:(这很明显吗?进一步的问题:有什么方法可以调试超出函数范围Python并进入基础C调用的方法?因为主Python分布很好,所以CPython,如果我的理解是正确的,Python则它只是api基础C代码的一个。通话最终Python会转换为C后台通话。因此,举例来说,我发现print C在C中定义如下,但是对我来说很难理解那里发生了什么(因为erm,我不知道C),但是也许通过调试器我可以打印出一些东西,看看是什么,并弄清楚至少是流程,如果不是全部的话。我非常想了解引擎盖下的总体情况,而不是理所当然地认为。
查看完整描述

2 回答

?
宝慕林4294392

TA贡献2021条经验 获得超8个赞

当答案非常简单时,您正在寻找的是非常复杂的东西。


我什至不知道“通向所有渠道”是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):

    print("stderring " + repr(obj))

    out = sys.stderr

    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 builtins

def print(*args, **kwargs):

    return builtins.print(*args, **kwargs)

或者,如果您担心print在其他模块中被调用,而不仅仅是在您的模块中被调用,您甚至可以将其隐藏在builtins:


builtins._print = builtins.print

def print(*args, **kwargs):

    return builtins._print(*args, **kwargs)

builtins.print = print

现在,您可以在Python级别使用pdb中断每个调用print,而不必担心C。


当然,您甚至可以在PyPy或Jython或其他任何工具中调试此代码,以查看它与“内置”级别以上的CPython是否有任何不同。


查看完整回答
反对 回复 2021-04-09
  • 2 回答
  • 0 关注
  • 128 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信