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



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

起初,我在函数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)

如果没有日志名称,则此错误为"值错误:对关闭的文件执行 I/O 操作"。关于如何在没有原始重复的情况下使其工作的任何建议?我可以重新打开 sys.stdout 吗?

您可以将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

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

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

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

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

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

如果已替换sys.stdout则可能需要改用sys.__stdout__

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

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

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

  • 包装构造
    • 包括异常处理
  • 开盘+收盘代码
  • 包装的代码

  • 仅包装"打开+关闭代码"是最简单的,另一种答案是一种可能的方法,但它留下了重复的withprint(........, file=fp)部分。

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

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

最新更新