在with语句关闭后重新打开sys.stdout

时间:2016-11-27 07:58:26

标签: python with-statement

我在使用PyYAML从yaml文件输入的打印信息时遇到问题。我正在尝试减少行数,而不会影响功能。在某些运行中,输出必须附加到文件中,而在其他运行中附加到stdout。

起初我在我的函数 processData

中多次使用它
labels

这很有效,但是当出现问题时,它的缺点是不使用 with 语句。

实际问题不是复杂的印刷语句,而是我

1)打印到文件或sys.stdout时不想复制代码 2)想要使用 with 语句,以便在出现打印错误时关闭文件 3)有几个这样的块,我不想为每个块调用不同的函数,因此防止代码重复

然后我尝试的是:

    if logName:
        fp = open(logName, 'a')
    else:
        fp = sys.stdout
    print(........, file=fp)
    print(........, file=fp)
    if logName:
        fp.close()

如果没有logName,则此错误为“ValueError:对已关闭文件的I / O操作”。有关如何在没有原始复制的情况下使其工作的任何建议?我可以重新打开sys.stdout吗?

2 个答案:

答案 0 :(得分:2)

您可以在类中“包装”sys.stdout以防止它首先被关闭。

with语句在开头和结尾分别对该类的实例调用__enter____exit__,因此请确保__exit__不执行任何操作:

class StdOut:
    def __enter__(self):
        return sys.stdout

    def __exit__(self, typ, val, trace):
        pass

stdout = StdOut()

然后使用stdout代替sys.stdout

答案 1 :(得分:1)

直译问题 - 重新打开标准输出

在最低C级别, stdout 是众所周知的file descriptor(指向运行时或系统管理的描述符表中的条目的整数),在进程中初始化在它的创作。 It cannot be reopened (with standard C means) once it's closed and must be duplicated beforehand if you still need it.

可以像这样创建sys.stdout的一次性副本:

stdout_copy=os.fdopen(os.dup(sys.stdout.fileno()),sys.stdout.mode)

(在Python 3中,os.fdopen()已合并到open()并且是它的别名。)

如果sys.__stdout__已被替换,您可能需要使用sys.stdout

另一个问题 - 将功能包装到with逻辑

首先,考虑标准的标准方法 - 即logging模块 - 以避免重新发明方形轮。按需打开和关闭文件可以很好地用它的机器实现,在绝大多数情况下甚至都不需要它。

现在,削减代码重复部分的唯一方法是将重复部分包装到子例程中(或者处理列表的代码块,其中的元素描述了每次迭代应该执行的操作,但它只能是用过一次)。这里有三个概念部分,无论语法如何(try / finally都与with一样):

  • 包装结构
    • 含。异常处理
  • 开场+结束代码
  • 包装代码
  • 仅包含“开始+结束代码”是最简单的,the other answer是一种可能的方式,但它会留下重复的withprint(........, file=fp)部分。

  • 包装整个结构更难,因为你必须将代码块传递给你想要的子程序,而Python故意省略匿名代码块 - 你必须def然后立即使用它相当尴尬。

    • 装饰器或将代码作为回调传递是两种可能。
    • 如果你的代码可以简化为一种模式(比如一组消息),你可以只传递那个模式并让子程序处理它。