почему возвращаемый объект os.popen не поддерживает вызов next()

#python #python-3.x

#python #python-3.x

Вопрос:

Документы Python: os.popen :

Откройте канал в команду или из команды. Возвращаемое значение представляет собой открытый файловый объект, подключенный к каналу, который может быть прочитан или записан в зависимости от того, установлен ли режим ‘r’ (по умолчанию) или ‘w’.

Я могу использовать next метод X.__next__() / X.next() (2.X), но не next(x) вызов,

  • разве __next__ метод и next(x) метод не совпадают?
  • почему мы не можем использовать next(x) объект for os.popen ?

И последнее, но не менее важное: как next() на самом деле работает next метод and ?

Комментарии:

1. Вы получаете какие-либо ошибки next() ?

2. да, ошибка типа: объект _wrap_close не является итератором

3. next(x) на самом деле работает в Python 2, но не работает в Python 3 для меня (за исключением, которое вы показываете).

4. books.google.co.kr/…

5. @vicious_101: тогда почему бы iter() сначала не вызвать его?

Ответ №1:

Глядя на исходный код (Python 3.4), кажется __next__ , что метод не реализован в _wrap_close классе, поэтому 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)
  

Комментарии:

1. Например, это итерируемый, а не итератор. Сначала вызовите iter() его.

2. @eryksun: верно, я на самом деле не заметил __getattr__ ; но специальные методы всегда просматриваются непосредственно по типу и __getattr__ не вызываются тогда. hasattr(type(os_open_return_value), 'next') имеет значение false, поэтому оболочка не является итератором. Если __iter__ бы вместо этого был возвращен self и прокси-сервер также предложил next() метод, все это сработало бы, но я предполагаю, что есть причина, по которой они этого не сделали? _wrap_close Используется для чего-то, что является только итерируемым, а не итератором?

3. @eryksun: ну, __iter__ не был реализован как return self , а скорее return iter(self._stream) . Ошибка в том, что прокси-сервер является итеративным, а не итератором.

4. Хорошо, я понял часть о os.popen() возврате os._wrap_close объекта, у которого нет __next__ метода. Однако я не понимаю вашего комментария о перезаписанном __getattr__ методе, потому что я не могу найти его в вашем примере кода. Не могли бы вы объяснить эту часть более подробно?

5. @Soong Когда мы вызываем next() экземпляр, он ищет __next__ метод в классе (не в экземпляре), поскольку в нем нет __next__ метода, _wrap_close вы получите сообщение об ошибке. С другой стороны, когда вы вызываете .__next__() экземпляр, Python ищет __next__ метод в экземпляре, а затем в классе, когда он не найден __getattr__ , вызывается, и оттуда они делегируют вызов базовому io.TextIOWrapper(proc.stdin) объекту.

Ответ №2:

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.txtn'