(just like simple files), it does not support the next built-in function, even though the
latter is supposed to simply call the former:
>>> I = os.popen('dir /B *.py')
>>> I.__next__()
'helloshell.py\n'
>>> I = os.popen('dir /B *.py')
>>> next(I)
TypeError: _wrap_close object is not an iterator
The reason for this is subtle—direct __next__ calls are intercepted by a __getattr__
defined in the pipe wrapper object, and are properly delegated to the wrapped object;
but next function calls invoke Python’s operator overloading machinery, which in 3.X
bypasses the wrapper’s __getattr__ for special method names like __next__. Since the
pipe wrapper object doesn’t define a __next__ of its own, the call is not caught and
delegated, and the next built-in fails. As explained in full in the book Learning Py-
thon, the wrapper’s __getattr__ isn’t tried because 3.X begins such searches at the class,
not the instance.
This behavior may or may not have been anticipated, and you don’t need to care if you
iterate over pipe lines automatically with for loops, comprehensions, and other tools.
To code manual iterations robustly, though, be sure to call the iter built-in first—this
invokes the __iter__ defined in the pipe wrapper object itself, to correctly support both
flavors of advancement:
>>> I = os.popen('dir /B *.py')
>>> I = iter(I) # what for loops do
>>> I.__next__() # now both forms work
'helloshell.py\n'
>>> next(I)
'more.py\n'
102 | Chapter 2: System Tools