为什么os.popen返回的对象不支持next()调用

5 投票
2 回答
4038 浏览
提问于 2025-04-18 10:44

Python文档:os.popen

这个功能可以打开一个管道,用于与命令进行数据交换。返回的结果是一个打开的文件对象,这个对象可以用来读取或写入数据,具体取决于你选择的模式是'r'(默认)还是'w'。

我可以使用next方法 X.__next__() / X.next()(在2.X版本中),但不能使用next(x)这个调用,

  • 难道__next__方法和next(x)不是一样的吗?
  • 为什么我们不能对os.popen的对象使用next(x)

最后,next()next这个方法到底是怎么工作的呢?

2 个回答

0

https://docs.python.org/2/library/functions.html#next 上关于 next 的说明是:

通过调用迭代器的 next() 方法来获取下一个项目。如果提供了默认值,当迭代器没有更多项目时就返回这个默认值,否则会抛出一个 StopIteration 的错误。

这个错误信息:

TypeError: _wrap_close object is not an iterator

表示它不是一个迭代器。很可能是缺少了 __iter__ 方法。

你遇到错误真是奇怪,因为在我用 Python 2.x 时,这段代码是可以正常工作的:

>>> next(os.popen('ls'))
'foo.txt\n'
6

看源代码(Python 3.4)的时候,发现_wrap_close这个类里没有实现__next__方法,所以当你调用next()的时候就会失败,因为找不到这个类里的__next__方法。而直接调用__next__是可以成功的,因为有一个重写的__getattr__方法。

相关的 源代码

def popen(cmd, mode="r", buffering=-1):
    if not isinstance(cmd, str):
        raise TypeError("invalid cmd type (%s, expected string)" % type(cmd))
    if mode not in ("r", "w"):
        raise ValueError("invalid mode %r" % mode)
    if buffering == 0 or buffering is None:
        raise ValueError("popen() does not support unbuffered streams")
    import subprocess, io
    if mode == "r":
        proc = subprocess.Popen(cmd,
                                shell=True,
                                stdout=subprocess.PIPE,
                                bufsize=buffering)
        return _wrap_close(io.TextIOWrapper(proc.stdout), proc)
    else:
        proc = subprocess.Popen(cmd,
                                shell=True,
                                stdin=subprocess.PIPE,
                                bufsize=buffering)
        return _wrap_close(io.TextIOWrapper(proc.stdin), proc)

# Helper for popen() -- a proxy for a file whose close waits for the process
class _wrap_close:
    def __init__(self, stream, proc):
        self._stream = stream
        self._proc = proc
    def close(self):
        self._stream.close()
        returncode = self._proc.wait()
        if returncode == 0:
            return None
        if name == 'nt':
            return returncode
        else:
            return returncode << 8  # Shift left to match old behavior
    def __enter__(self):
        return self
    def __exit__(self, *args):
        self.close()
    def __getattr__(self, name):
        return getattr(self._stream, name)
    def __iter__(self):
        return iter(self._stream)

撰写回答