<p>当答案非常简单的时候,你在寻找一些非常复杂的东西。你知道吗</p>
<p>我甚至不知道“管道到所有通道”是什么意思,但是<code>print</code>并没有这样做。它所做的就是调用传递给它的<code>write</code>对象。你知道吗</p>
<p>但是,它为每个参数调用一次<code>write</code>,为每个<code>sep</code>调用一次,为<code>end</code>调用一次。你知道吗</p>
<p>所以,这一行:</p>
<pre><code>print("Don't you ever disrespect the caterpillar", file=debug)
</code></pre>
<p>…大致相当于:</p>
<pre><code>debug.write(str("Don't you ever disrespect the caterpillar"))
debug.write("\n")
</code></pre>
<p>…这当然意味着你会收到两次额外的<code>print</code>信息。你知道吗</p>
<hr/>
<p>顺便说一句,为了将来调试或理解这样的事情:如果您将额外的<code>print</code>更改为包含,比如说<code>repr(obj)</code>,发生的事情将是显而易见的:</p>
<pre><code>def _debugwrite(obj):
print("stderring " + repr(obj))
out = sys.stderr
out.write(obj)
</code></pre>
<p>然后输出为:</p>
<pre><code>stderring "Don't you ever disrespect the caterpillar"
stderring '\n'
Don't you ever disrespect the caterpillar
</code></pre>
<p>不再那么神秘了,对吧?你知道吗</p>
<hr/>
<p>当然<code>stdout</code>和<code>stderr</code>是独立的流,有各自的缓冲区。(默认情况下,当与TTY交谈时,<code>stdout</code>是行缓冲的,<code>stderr</code>是无缓冲的。)因此排序不是您天真地期望的,而是有意义的。如果只添加<code>flush</code>es,则输出将变成:</p>
<pre><code>stderring "Don't you ever disrespect the caterpillar"
Don't you ever disrespect the caterpillarstderring '\n'
</code></pre>
<p>(结尾有一个空行)。你知道吗</p>
<hr/>
<p>对于您的奖励问题:</p>
<blockquote>
<p>I tried to use inspect module to get the caller, maybe see who does the actual call to write but I get module, idk why :( is this obvious?</p>
</blockquote>
<p>我假设你做了类似于<code>inspect.stack()[1].function</code>的事?如果是这样,那么您要检查的代码就是模块中的顶级代码,因此<code>inspect</code>将其显示为名为<code><module></code>的伪函数。你知道吗</p>
<blockquote>
<p>Is there any way to debug a function beyond Python and go into the underlying C call?</p>
</blockquote>
<p>当然。只需在lldb、gdb、微软的调试器或其他通常用于调试二进制程序的工具下运行CPython本身。您可以将断点放在<code>ceval</code>循环或特定的C API函数中或任何您想要的地方。您可能希望对CPython进行调试构建(do<code>./configure help</code>以查看选项),从而使其更加完善。你知道吗</p>
<blockquote>
<p>Because well the main Python distribution, is CPython, and if my understanding is correct, Python is just an api for the underlying C code.</p>
</blockquote>
<p>嗯,不是很好。它是一个编译器和一个字节码解释器。字节码解释器<em>很大程度上</em>使用了为扩展/嵌入接口公开的相同的C API,但是重叠不是100%;有些地方它处理的结构低于C API级别。你知道吗</p>
<blockquote>
<p>A call in Python gets translated to a C call under the hood eventually. So for instance I found out that the print is defined as follows in C, but it's tough for me to understand what's going on there (because, erm, I don't know C) but maybe by going with a debugger I could print stuff out, see what is what and figure out maybe at least the flow if not everything. I'd very much like to understand what's going on under the hood in general instead of taking stuff for granted.</p>
</blockquote>
<p>是的,您可以这样做,但是您需要同时了解C和cpythonapi(例如,如何找到等价于<code>__call__</code>的C插槽),以确定在何处放置断点并开始跟踪。你知道吗</p>
<p>对于这样的情况,用Python编写包装器并用Python调试它们要容易得多。例如:</p>
<pre><code>import builtins
def print(*args, **kwargs):
return builtins.print(*args, **kwargs)
</code></pre>
<p>或者,如果您担心<code>print</code>在其他模块中被调用,而不仅仅是在您的模块中,您甚至可以将它隐藏在<code>builtins</code>:</p>
<pre><code>builtins._print = builtins.print
def print(*args, **kwargs):
return builtins._print(*args, **kwargs)
builtins.print = print
</code></pre>
<p>现在您可以在Python级别使用<code>pdb</code>中断对<code>print</code>的每次调用,而不用担心C</p>
<p>当然,您甚至可以在PyPy或Jython中调试这些代码,看看它是否与“内置”级别以上的CPython有所不同。你知道吗</p>