4

我经常遇到这样的情况,即根据某些命令行参数,输入可能来自文件或标准输入。输出也是如此。我真的很喜欢 python 3 中的上下文管理器的工作方式,因此尝试将我的所有open调用作为某些with语句的一部分。但在这种情况下,我遇到了麻烦。

if args.infile:
    with open(args.infile, "r") as f:
        process(f)
else:
    process(sys.stdin)

已经很笨拙了,输入和输出我都必须满足四种组合。我想要一些更简单的东西,例如

with (open(args.infile, "r") if args.infile
      else DummyManager(sys.stdin)) as f:
    process(f)

python 标准库中是否有类似 DummyManager 的东西?实现上下文管理器协议但仅从其__enter__方法返回固定值的东西?我猜这样一个类最有可能的位置是contextlib,因为我在那里没有找到类似的东西,所以也许没有这样的东西。您可以建议其他优雅的解决方案吗?

4

4 回答 4

2

@contextlib.contextmanager用装饰器创建一个很简单:

from contextlib import contextmanager

@contextmanager
def dummy_manager(ob):
    yield ob

而已; 这会创建一个上下文管理器,它只会将您ob交还给您,而__exit__处理程序则什么也不做。

我会这样使用它:

f = open(args.infile, "r") if args.infile else dummy_manager(sys.stdin)
with f:
    process(f)
于 2014-04-25T19:11:46.120 回答
2

在您的情况下,您可以使用fileinput模块

from fileinput import FileInput

with FileInput(args.infile) as file:
    process(file)
# sys.stdin is still open here

如果args.infile='-'那么它使用sys.stdin. 如果inplace=True是参数,那么它会重定向sys.stdout到输入文件。您可以传递多个文件名。如果没有文件名,它使用在命令行或标准输入中给出的文件名。

或者您可以将文件保持原样:

import sys
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--log', default=sys.stdout, type=argparse.FileType('w'))
args = parser.parse_args()
with args.log:
    args.log.write('log message')
# sys.stdout may be closed here

对于大多数可以使用标准输出来写入结果的程序来说应该没问题。

为避免关闭sys.stdin / sys.stdout,您可以使用ExitStack有条件地启用上下文管理器:

from contextlib import ExitStack

with ExitStack() as stack:
    if not args.files:
       files = [sys.stdin]
    else:
       files = [stack.enter_context(open(name)) for name in args.files]

    if not args.output:
       output_file = sys.stdout
       stack.callback(output_file.flush) # flush instead of closing 
    else:
       output_file = stack.enter_context(open(args.output, 'w'))

    process(files, output_file)
于 2014-04-25T21:51:28.187 回答
1

在您的情况下,您不需要虚拟经理。 sys.stdin,类似于文件,可以用作上下文管理器。

with (open(args.infile, "r") if args.infile else sys.stdin) as f:
    process(f)

需要注意的一件事是,当__exit__ing 块时, sys.stdin 得到closed (而您通常不需要/不想自己关闭它),但这应该不是问题。

于 2014-04-25T19:38:52.430 回答
0

ArgParse现在存在各种包装器和替代品,它们很好地支持了这一点。我喜欢click

with click.open_file(filename) as lines:
    for line in lines:
        process(line)

这将处理sys.stdinif filenameis-否则回退到在上下文管理open器部分close隐藏的常规。finally

于 2018-04-13T06:05:31.650 回答