当前位置: 首页 > 面试题库 >

装饰\委派File对象以添加功能

廉展鹏
2023-03-14
问题内容

我正在编写一个小的Python脚本,该脚本使用该subprocess模块和一个辅助函数来执行一些Shell命令:

import subprocess as sp
def run(command, description):
    """Runs a command in a formatted manner. Returns its return code."""
    start=datetime.datetime.now()
    sys.stderr.write('%-65s' % description)
    s=sp.Popen(command, shell=True, stderr=sp.PIPE, stdout=sp.PIPE)
    out,err=s.communicate()
    end=datetime.datetime.now()
    duration=end-start
    status='Done' if s.returncode==0 else 'Failed'
    print '%s (%d seconds)' % (status, duration.seconds)

以下几行显示标准输出和错误:

    s=sp.Popen(command, shell=True, stderr=sp.PIPE, stdout=sp.PIPE)
    out,err=s.communicate()

如您所见,未使用stdout和stderr。假设我想以格式化的方式将输出和错误消息写入日志文件,例如:

[STDOUT: 2011-01-17 14:53:55] <message>
[STDERR: 2011-01-17 14:53:56] <message>

我的问题是,最Python化的方法是什么?我想到了三个选择:

  1. 继承文件对象并覆盖write方法。
  2. 使用实现的Delegate类write
  3. PIPE某种方式连接到自身。

更新:参考测试脚本

我正在使用此脚本检查结果,该脚本另存为test.py

#!/usr/bin/python
import sys

sys.stdout.write('OUT\n')
sys.stdout.flush()
sys.stderr.write('ERR\n')
sys.stderr.flush()

有任何想法吗?


问题答案:

1和2是合理的解决方案,但仅重写write()是不够的。

问题是Popen需要将文件句柄附加到进程,因此Python文件对象不起作用,它们必须是OS级别的。要解决这个问题,您必须拥有一个具有os级文件句柄的Python对象。我能想到的解决此问题的唯一方法是使用管道,因此您要写入一个os级文件句柄。但是,然后您需要另一个线程来坐下来并轮询该管道以供读取内容,以便可以对其进行记录。(因此,严格来说,这是2的实现,因为它委派了日志记录)。

说完了:

import io
import logging
import os
import select
import subprocess
import time
import threading

LOG_FILENAME = 'output.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)

class StreamLogger(io.IOBase):
    def __init__(self, level):
        self.level = level
        self.pipe = os.pipe()
        self.thread = threading.Thread(target=self._flusher)
        self.thread.start()

    def _flusher(self):
        self._run = True
        buf = b''
        while self._run:
            for fh in select.select([self.pipe[0]], [], [], 0)[0]:
                buf += os.read(fh, 1024)
                while b'\n' in buf:
                    data, buf = buf.split(b'\n', 1)
                    self.write(data.decode())
            time.sleep(1)
        self._run = None

    def write(self, data):
        return logging.log(self.level, data)

    def fileno(self):
        return self.pipe[1]

    def close(self):
        if self._run:
            self._run = False
            while self._run is not None:
                time.sleep(1)
            os.close(self.pipe[0])
            os.close(self.pipe[1])

这样,该类将启动os级管道,Popen可以将stdin / out /
error附加到该子进程。它还启动一个线程,该线程每秒轮询该管道的另一端以进行记录,然后将其记录到日志记录模块中。

此类可能为了实现完整性而应实现更多功能,但无论如何在这种情况下仍然有效。

示例代码:

with StreamLogger(logging.INFO) as out:
    with StreamLogger(logging.ERROR) as err:
        subprocess.Popen("ls", stdout=out, stderr=err, shell=True)

output.log最终如下所示:

INFO:root:output.log
INFO:root:streamlogger.py
INFO:root:and
INFO:root:so
INFO:root:on

经过Python 2.6、2.7和3.1测试。

我认为1和3的任何实现都需要使用类似的技术。它有点涉及,但是除非您可以正确地使Popen命令本身记录日志,否则我没有更好的主意)。



 类似资料:
  • 委托 装饰者模式的另一种实现方案是委托。在这种机制下,一个对象可以和另一个对象相关联。比如你在用 UITableView ,你必须实现 tableView(_:numberOfRowsInSection:) 这个委托方法。 你不应该指望 UITableView 知道你有多少数据,这是个应用层该解决的问题。所以,数据相关的计算应该通过 UITableView 的委托来解决。这样可以让 UITable

  • 从实体映射时,我想向DTO/model对象添加一个派生列表。例如,我有一个名为Company的实体类,它有一个员工列表。我希望公司模型对象包含员工列表和经理员工列表。(这不是我真正的类名,但这是我正在尝试做的事情)。所以,我希望公司数据有一个员工列表和一个经理列表,其中经理是通过过滤员工列表而得到的员工子集。 我尝试使用映射表达式和默认方法。然而,我需要使用EmployeeMapper,而我似乎没

  • 看过 angular2 的人基本都知道,它大量使用了装饰器,而装饰器还属于 ecmascript 的征集意愿的第一阶段。 使用装饰器,需要在 tsconfig.json 里面开启支持选项 experimentalDecorators // tsconfig.json "experimentalDecorators": true 装饰器基本可以对所有变量起作用,它的语法是 @装饰器 ,这个装饰器必须

  • django.core.files模块及其子模块包含了一些用于基本文件处理的内建类。 File类 class File(file_object) File 类是Python file 对象的一个简单封装,并带有Django特定的附加功能。需要表示文件的时候,Django内部会使用这个类。 File对象拥有下列属性和方法: name 含有MEDIA_ROOT相对路径的文件名称。 size 文件的字节

  • 为了让用户能够通过HTML5的file API直接操作本地文件,DOM的File接口提供了对本地文件的抽象。Electron在File接口中增加了一个path属性,它是文件在系统中的真实路径。 获取拖动到APP中文件的真实路径的例子: <div id="holder"> Drag your file here </div> <script> var holder = document.g

  • 在文件系统中,使用HTML5 File 原生API操作文件 DOM 文件接口为原生文件提供了抽象, 以便让用户使用 HTML5 文件 API 直接处理原生文件。 Electron已经向 文件 接口添加了一个 path 属性, 在文件系统上暴露出文件的真实路径 示例:获取拖拽到app上的文件的真实路径 <div id="holder"> Drag your file here </div> <s