在单线程Python脚本中使用控制台

3 投票
2 回答
2282 浏览
提问于 2025-04-16 07:25

我想在一个单线程的脚本中使用一个可以互动的控制台,同时还要保持几个TCP连接是打开的。这样的话,我就不能让标准输入一直占用这个线程。

有没有简单的方法可以做到这一点?还是说我应该把控制台放在自己的线程里,这样就解决了?

2 个回答

0

你可以选择单线程或者多线程都可以,但如果你决定不使用线程,那你就需要用轮询的方式来检查。比如在C语言中,可以用poll(2)这个函数来实现。这样你就可以查看控制台或者TCP连接是否有输入准备好了。

3

你可以从内置的 'code' 模块中继承 InteractiveConsole 类,并重写它的 push() 方法,做一个包装器,这个包装器会把标准输出和错误输出重定向到一个 StringIO 实例,然后再把这些内容传递给基类的 push() 方法。你的包装器可以返回一个包含两个元素的元组(more, result),其中 'more' 表示 InteractiveConsole 是否还需要更多输入,而 'result' 则是 InteractiveConsole.push() 写入到你的 StringIO 实例中的内容。

听起来可能有点复杂,但其实并不难。基本的思路是这样的:

import sys
from cStringIO import StringIO
from code import InteractiveConsole
from contextlib import contextmanager

__all__ = ['Interpreter']


@contextmanager
def std_redirector(stdin=sys.stdin, stdout=sys.stdin, stderr=sys.stderr):
    """Temporarily redirect stdin/stdout/stderr"""

    tmp_fds = stdin, stdout, stderr
    orig_fds = sys.stdin, sys.stdout, sys.stderr
    sys.stdin, sys.stdout, sys.stderr = tmp_fds
    yield
    sys.stdin, sys.stdout, sys.stderr = orig_fds


class Interpreter(InteractiveConsole):
    """Remote-friendly InteractiveConsole subclass

    This class behaves just like InteractiveConsole, except that it
    returns all output as a string rather than emitting to stdout/stderr

    """
    banner = ("Python %s\n%s\n" % (sys.version, sys.platform) +
              'Type "help", "copyright", "credits" or "license" '
              'for more information.\n')

    ps1 = getattr(sys, "ps1", ">>> ")
    ps2 = getattr(sys, "ps2", "... ")


    def __init__(self, locals=None):
        InteractiveConsole.__init__(self, locals=locals)
        self.output = StringIO()
        self.output = StringIO()

    def push(self, command):
        """Return the result of executing `command`

        This function temporarily redirects stdout/stderr and then simply
        forwards to the base class's push() method.  It returns a 2-tuple
        (more, result) where `more` is a boolean indicating whether the
        interpreter expects more input [similar to the base class push()], and
        `result` is the captured output (if any) from running `command`.

        """
        self.output.reset()
        self.output.truncate()
        with std_redirector(stdout=self.output, stderr=self.output):
            try:
                more = InteractiveConsole.push(self, command)
                result = self.output.getvalue()
            except (SyntaxError, OverflowError):
                pass
            return more, result

这里有一个完整的例子,它可以从一个 UDP 套接字接收输入:

启动两个控制台,在一个中运行 server.py,在另一个中运行 client.py。你在 client.py 中看到的内容应该和 Python 的常规交互式解释器没有区别,尽管所有的命令都是通过 server.py 来进行评估的。

当然,像这样使用套接字是非常不安全的,但它展示了如何异步地评估外部输入。只要你信任输入源,你应该能够将其调整到你的情况。当有人输入时,事情会变得“有趣”:

while True: continue

但那完全是另一个问题... :-)

撰写回答