【问题标题】:Creating a custom sys.stdout class?创建自定义 sys.stdout 类?
【发布时间】:2010-02-19 16:35:41
【问题描述】:

我想要做的只是将一些终端命令的输出打印到 wx.TextCtrl 小部件。我认为最简单的方法是创建一个自定义标准输出类并将写入函数重载到小部件的写入函数。

标准输出类:

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

任何解决此问题的想法将不胜感激。

【问题讨论】:

    标签: python stdout


    【解决方案1】:

    sys.stdout不是class,它是实例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,只需像这样简单的类文件对象! Duck typing 是你的朋友...

    (请注意,在 Python 中,与大多数其他 OO 语言一样,但与 Javascript 不同,您只能从类 AKA 类型继承,从不从类/类型的实例; -)。

    【讨论】:

    • 我试过这个,结果是: Traceback(最近一次调用最后一次):文件“blade_gui.py”,第 179 行,在 OnNew subprocess.Popen('echo "Hello World!"', stdout=sys.stdout,shell=True) 文件“C:\Python26\lib\subprocess.py”,第 614 行,在 init errread, errwrite) = self._get_handles(stdin, stdout, stderr ) 文件“C:\Python26\lib\subprocess.py”,第 734 行,在 _get_handles c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) AttributeError: 'StdOut' object has no attribute 'fileno'
    • subprocess 可以执行用任何语言(不仅仅是 Python!)编写的程序,因此需要一个“真正的操作系统级文件”(即带有 FD 的文件,也称为 fileno)——除了真正的操作系统级文件(包括,幸运的是,subprocess.PIPE,它可以让您的父进程接收子进程的输出 - 但是,通常在子进程中缓冲意味着这不是你想要的,所以我总是建议使用 pexpectwexpect 代替)。
    • 你需要的不仅仅是write,你还需要flush,也许还有一些其他的,这取决于它的使用方式。
    • @Duck - 我写了一个应该处理更多案例的示例。请看我的回答。如果有任何其他极端情况,请告诉我,我很乐意修改。
    【解决方案2】:

    如果您只需要实现编写,则根本不需要定义一个新类。直接用createdTxtCtrl代替StdOut(createdTxtCtrl),因为前者已经支持你需要的操作了。

    如果您需要对 stdout 做的只是将一些程序的输出定向到那里,而不是将各种东西定向到那里,请不要更改 sys.stdout,只需使用您自己的类文件对象实例化 subprocess.Popen(@ 987654325@) 而不是sys.stdout

    【讨论】:

      【解决方案3】:

      我已经为 PyPi 部署了一个名为 tiotrap 的包,其中包含用于管理 TextIO 流的 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")
      

      【讨论】:

        【解决方案4】:

        sys.stdout = StdOut(createdTxtCtrl) 不会创建循环依赖吗?尽量不要将sys.stdout 重新分配给派生自sys.stdout 的类。

        【讨论】:

          猜你喜欢
          • 2015-11-02
          • 2012-12-29
          • 1970-01-01
          • 1970-01-01
          • 2015-10-20
          • 2011-06-23
          • 2021-12-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多