Python中使用subprocess的上下文管理器简单解释是什么?
我是一名活跃的Python用户,发现网上大多数教程都让我使用上下文管理器和子进程来通过Python执行二进制文件,比如用exiftool读取图片的exif数据。
def readExif(fname):
with ExifTool() as e:
try:
metadata = e.get_metadata(fname)
metadata = metadata[0]
except UnicodeDecodeError:
return False
for f in files:
print readExif(f)
我的问题是(从一个真正的初学者的角度):为什么我不能直接使用os.system()呢?
2 个回答
这要看你在子进程中想要做什么。如果你只是想执行一些不需要互动的命令,使用os.system()就可以了。但很多时候,你可能需要或想要读取输出信息(stdout)和错误信息(stderr),并且希望子进程能和主进程同时运行。使用上下文管理器会更灵活,适合处理比简单命令执行更复杂的情况。
ExifTool
这个装饰器主要是为了简化代码,省去很多繁琐的步骤和错误处理。如果你直接用像os.system()
这样的方式来调用exiftool这个程序,你的代码会显得很复杂。即使你用subprocess.Popen
,这比os.system
高级一点,代码也会变得很冗长,跟ExifTool
提供的功能相比,效果差不多,但看起来会复杂很多:
with open(os.devnull, "w") as devnull:
_process = subprocess.Popen(
[self.executable, "-stay_open", "True", "-@", "-",
"-common_args", "-G", "-n"],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=devnull)
try:
_process.stdin.write(b"\n".join([filename] + (b"-execute\n",)))
_process.stdin.flush()
output = b""
fd = _process.stdout.fileno()
sentinel = b"{ready}"
while not output[-32:].strip().endswith(sentinel):
output += os.read(fd, block_size)
metadata = output.strip()[:-len(sentinel)][0]
metadata = metdata[0]
except UnicodeDecodeError:
return False
finally:
_process.stdin.write(b"-stay_open\nFalse\n")
_process.stdin.flush()
_process.communicate()
del _process
(这个代码完全没有测试,可能根本不能用。我只是快速查看了pyexiftool的源代码,随便写的。)
从上面可以看到,里面有很多内容。上下文管理器在try
块之前和整个finally
块中处理了所有事情。如果你想在不使用上下文管理器的情况下使用ExifTool
,但又想实现相同的功能,代码会是这样的:
e = ExifTool()
e.start()
try:
metadata = e.get_metadata(fname)
metadata = metadata[0]
except UnicodeDecodeError:
return False
finally:
e.terminate()
你可以通过查看ExifTool.__enter__()
和ExifTool.__exit__()
来确认这一点,这两个方法分别是在你进入和退出with ExifTool() as e:
块时被调用的。
当然,有很多简单的情况,直接用os.system
执行子进程也能正常工作(不过一般来说,我建议使用subprocess
模块)。你关注的这些特定例子恰好比较复杂,因此更能体现面向对象编程和上下文管理器带来的好处。