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

问题描述 投票:0回答:2

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

起初我在函数中多次使用了这个processData

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

这有效,但有一个缺点,就是当出现问题时不使用 with 语句。

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

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

那么我尝试的是:

def processData(yamlData, logName=None):
    ......
    with open(logName, 'a') if logName else sys.stdout as fp:
        print(........, file=fp)
        print(........, file=fp)
    .....
    with open(logName, 'a') if logName else sys.stdout as fp:
        print(........, file=fp)
        print(........, file=fp)

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

python with-statement
2个回答
3
投票

您可以将

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


3
投票

字面问题 - 重新打开标准输出

在最低的 C 级别,stdout 是众所周知的文件描述符(指向运行时或系统管理的描述符表中的条目的整数),在创建时在进程中进行初始化。 一旦关闭就无法重新打开(使用标准 C 方式),如果您仍然需要它,必须事先复制。

可以像这样创建

sys.stdout
的一次性副本:

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

(在 Python 2 中,使用

os.fdopen()
代替
open
,并且不需要指定模式,因为它能够从描述符中推断出模式。)

如果

sys.__stdout__
已被替换,您可能需要使用
sys.stdout
来代替。

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

首先,考虑标准的日志方式 - 即

logging
模块 - 以避免重新发明方轮。按需打开和关闭文件可以通过其机制很好地实现,并且在绝大多数情况下甚至不需要。

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

try
/
finally
以及
with
):

  • 包装结构
    • 包括。异常处理
  • 打开+关闭代码
  • 包装后的代码

  • 仅包装“开始+结束代码”是最简单的,另一个答案是一种可能的方法,但它留下了重复的

    with
    print(........, file=fp)
    部分。

  • 包装整个构造更加困难,因为您必须将代码块传递给您想要的子例程,并且Python故意省略匿名代码块 - 您必须

    def
    然后立即使用它,这是相当尴尬的.

    • 装饰器或将代码作为回调传递是两种可能性。
    • 如果您的代码可以简化为一种模式(如一组消息),您可以只传递该模式并让子例程处理它。
© www.soinside.com 2019 - 2024. All rights reserved.