创建自定义的 sys.stdout 类?

7 投票
4 回答
8450 浏览
提问于 2025-04-15 19:29

我想做的事情很简单,就是把一些终端命令的输出显示到一个wx.TextCtrl控件里。我想,最简单的方法就是创建一个自定义的stdout类,并把它的写入功能重载成这个控件的写入功能。

stdout类:

class StdOut(sys.stdout):
    def __init__(self,txtctrl):
        sys.stdout.__init__(self)
        self.txtctrl = txtctrl

    def write(self,string):
        self.txtctrl.write(string)

然后我会这样做:

sys.stdout = StdOut(createdTxtCtrl)    
subprocess.Popen('echo "Hello World!"',stdout=sys.stdout,shell=True)

结果出现了以下错误:

Traceback (most recent call last):
File "mainwindow.py", line 12, in <module>
from systemconsole import SystemConsole
File "systemconsole.py", line 4, in <module>
class StdOut(sys.stdout):
TypeError: Error when calling the metaclass bases
file() argument 2 must be string, not tuple

如果有任何解决这个问题的想法,我会很感激。

4 个回答

0

我在PyPi上发布了一个叫做 tiotrap 的包,这个包里面有一个叫 TextIOTrap 的辅助类,用来管理文本输入输出流。

安装方法:

python3 -m pip install tiotrap

这个Trap会把数据写入一个列表,你也可以用这个方法来实现自己的处理方式,只需要把lambda替换成一个函数或方法的调用就可以了:

import tiotrap
aTrappedStdout = []
_stdout_bk = sys.stdout

sys.stdout = tiotrap.TextIOTrap(write_handler=lambda s: aTrappedStdout.append(s))

print("TEST1")
print("TEST2")

sys.stdout = _stdout_bk
print(f"aTrappedStdout = {aTrappedStdout}")
# output: aTrappedStdout = ['TEST1', 'TEST2']

下面是一个使用它来存储捕获数据的方法:

_stdout_bk = sys.stdout

tio_trap = tiotrap.TextIOTrap(store=True)

sys.stdout = tio_trap
print("TEST1")
print("TEST2")
sys.stdout = _stdout_bk

print(f"captured logs:\n{str(tio_trap)}\n~end~\n")
# output: 
# captured logs:
# TEST1
# TEST2
# ~end~

它还可以作为跨平台的DEVNULL替代品:

_stdout_bk = sys.stdout
sys.stdout = tiotrap.DEVNULL
print("THIS WILL NOT PRINT")
sys.stdout = _stdout_bk
print("THIS WILL PRINT")
0

如果你只需要实现写入功能,其实根本不需要定义一个新类。你可以直接使用 createdTxtCtrl,而不是 StdOut(createdTxtCtrl),因为前者已经支持你需要的操作。

如果你只是想把一些程序的输出导向标准输出,而不是把各种东西都导过去,那就不要去改 sys.stdout。只需要用你自己的文件样的对象(createdTxtCtrl)来实例化 subprocess.Popen,而不是用 sys.stdout

17

sys.stdout 其实不是一个 ,而是一个实例(它的类型是 file)。

所以,你只需要这样做:

class StdOut(object):
    def __init__(self,txtctrl):
        self.txtctrl = txtctrl
    def write(self,string):
        self.txtctrl.write(string)

sys.stdout = StdOut(the_text_ctrl)

不需要从 file 继承,只要像这样简单地创建一个类似文件的对象就可以了!鸭子类型会帮到你……

(注意,在Python中,和大多数其他面向对象的语言一样,但和Javascript不同,你只会从类,也就是类型中继承,绝对不会从类/类型的实例中继承;-)。

撰写回答