Python select () ведет себя странно - PullRequest
6 голосов
/ 16 мая 2011

У меня возникли проблемы с пониманием поведения select.select. Пожалуйста, рассмотрите следующую программу Python:

def str_to_hex(s):
    def dig(n):
        if n > 9:
            return chr(65-10+n)
        else:
            return chr(48+n)
    r = ''
    while len(s) > 0:
        c = s[0]
        s = s[1:]
        a = ord(c) / 16
        b = ord(c) % 16
        r = r + dig(a) + dig(b)
    return r

while True:
    ans,_,_ = select.select([sys.stdin],[],[])
    print ans
    s = ans[0].read(1)
    if len(s) == 0: break
    print str_to_hex(s)

Я сохранил это в файл "test.py". Если вызвать его следующим образом:

echo 'hello' | ./test.py

тогда я получаю ожидаемое поведение: выберите никогда не блокировать, и все данные будут напечатаны; Затем программа завершается.

Но если я запускаю программу в интерактивном режиме, я получаю самое нежелательное поведение. Пожалуйста, рассмотрите следующую сессию консоли:

$ ./test.py
hello
[<open file '<stdin>', mode 'r' at 0xb742f020>]
68

Затем программа висит там; select.select теперь снова блокируется. Только когда я предоставлю больше ввода или закрою поток ввода, будет напечатан следующий символ (и все остальные), даже если уже есть ожидающие символы! Может кто-нибудь объяснить мне это поведение? Я вижу нечто подобное в написанной мною программе потокового туннелирования, и все это разрушает.

Спасибо за чтение!

Ответы [ 2 ]

9 голосов
/ 16 мая 2011

Метод read для sys.stdin работает на более высоком уровне абстракции, чем select.Когда вы делаете ans[0].read(1), python фактически читает большее количество байтов из операционной системы и буферизует их внутренне.select не знает об этой дополнительной буферизации;Он только видит, что все прочитано, и поэтому будет блокироваться, пока не поступит EOF или больше ввода.Вы можете наблюдать это поведение, запустив что-то вроде strace -e read,select python yourprogram.py.

. Одним из решений является замена ans[0].read(1) на os.read(ans[0].fileno(), 1).os.read - это интерфейс более низкого уровня без какой-либо буферизации между ним и операционной системой, поэтому он лучше подходит для select.

В качестве альтернативы, запуск python с опцией командной строки -u также можетотключить дополнительную буферизацию.

1 голос
/ 16 мая 2011

Он ожидает вашего сигнала EOF (вы можете сделать это с помощью Ctrl + D, когда используется в интерактивном режиме).Вы можете использовать sys.stdin.isatty(), чтобы проверить, выполняется ли скрипт в интерактивном режиме, и соответственно обработать его, используя вместо этого, скажем, raw_input.Я также сомневаюсь, что вам вообще нужно использовать select.select, почему бы просто не использовать sys.stdin.read?

if sys.stdin.isatty():
    while True:
        for s in raw_input():
            print str_to_hex(s)
else:
    while True:
        for s in sys.stdin.read(1):
            print str_to_hex(s)

, который бы подходил как для интерактивного использования, так и для потоковой обработки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...