如何等待按键输入?

786

我该如何让我的Python脚本等待用户按下任意键?

13个回答

809

Python 3中,使用input()函数:

input("Press Enter to continue...")

Python 2 中,使用 raw_input():

raw_input("Press Enter to continue...")

这只等待用户按下回车键。


在Windows/DOS上,可能需要使用msvcrtmsvcrt模块为您提供了访问Microsoft Visual C/C++运行时库(MSVCRT)中多个函数的方法:

import msvcrt as m
def wait():
    m.getch()

这应该等待按键操作。


注:

在 Python 3 中,raw_input() 不再存在。
在 Python 2 中,input(prompt) 等同于 eval(raw_input(prompt))


58
当我尝试在Python 2.7中执行此操作时,出现以下错误:"SyntaxError:解析时意外的EOF"。 - Jon Tirsen
8
@Solarsaturn9 和越来越多的人并不这样认为。因此对我以及其他很多人来说,这个答案都不起作用。 - ctrl-alt-delor
5
@richard使用input()在其他平台上也应该可以工作。提供一个只适用于Windows的替代解决方案而扣分是荒谬的,因为第一个解决方案是跨平台的。 - Cory Buckley
9
请再次阅读问题和答案:“input” 只有在按 Enter 键时才会继续,如果按下任何键,则不会继续。我希望你能以更通俗易懂的语言表达出来,但不改变原意。请勿提供解释或其他非翻译内容。需要翻译的内容为:@Solarsaturn9 read the question and answer again: input does not continue if any key is pressed, only if enter is pressed. - ctrl-alt-delor
13
由于Python 2.7有一个名为input的函数,它会对你输入的字符串进行求值,所以才会出现这种情况。要解决这个问题,请使用raw_input。 - Samie Bencherif
显示剩余3条评论

338
在Python 3中,请使用input()
input("Press Enter to continue...")

在Python 2中,请使用raw_input()

raw_input("Press Enter to continue...")

22
当键值不仅限于 enter 键时,该怎么办? - noio
使用six实现Py2和Py3兼容代码:from six.moves import input; input("按Enter键继续...") - rcoup

62

在我的Linux系统上,我使用以下代码。这段代码与我在其他地方看到的代码类似(例如,在旧版Python常见问题解答中),但是那段代码会在一个紧密循环中运行,而这段代码不会,并且有许多奇怪的边角情况那段代码没有考虑到,而这段代码却做到了。

def read_single_keypress():
    """Waits for a single keypress on stdin.

    This is a silly function to call if you need to do it a lot because it has
    to store stdin's current setup, setup stdin for reading single keystrokes
    then read the single keystroke then revert stdin back after reading the
    keystroke.

    Returns a tuple of characters of the key that was pressed - on Linux, 
    pressing keys like up arrow results in a sequence of characters. Returns 
    ('\x03',) on KeyboardInterrupt which can happen when a signal gets
    handled.

    """
    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    # save old state
    flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
    attrs_save = termios.tcgetattr(fd)
    # make raw - the way to do this comes from the termios(3) man page.
    attrs = list(attrs_save) # copy the stored version to update
    # iflag
    attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                  | termios.ISTRIP | termios.INLCR | termios. IGNCR
                  | termios.ICRNL | termios.IXON )
    # oflag
    attrs[1] &= ~termios.OPOST
    # cflag
    attrs[2] &= ~(termios.CSIZE | termios. PARENB)
    attrs[2] |= termios.CS8
    # lflag
    attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                  | termios.ISIG | termios.IEXTEN)
    termios.tcsetattr(fd, termios.TCSANOW, attrs)
    # turn off non-blocking
    fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
    # read a single keystroke
    ret = []
    try:
        ret.append(sys.stdin.read(1)) # returns a single character
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save | os.O_NONBLOCK)
        c = sys.stdin.read(1) # returns a single character
        while len(c) > 0:
            ret.append(c)
            c = sys.stdin.read(1)
    except KeyboardInterrupt:
        ret.append('\x03')
    finally:
        # restore old state
        termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
    return tuple(ret)

1
虽然这是我在这里最喜欢的答案之一,但与其他答案一样,它无法捕捉到Shift、Control等内容。 - Mala
2
@Mala 在纯Python中几乎不可能实现这个功能;也许你应该编写一个C模块? - cat
1
ctrl-c 是 ASCII 3,所以这是预期的。如果你想在按下 ctrl-c 时触发一个信号,简单的解决方案是加上 if ord(returned_value) == 3: os.kill(os.getpid(), signal.SIGINT)。但你也可以通过 attrs[0] |= termios.BRKINT, attrs[3] != termios.ISIG 来关闭信号处理,并且去掉 except KeyboardInterrupt 处理。注意 - 我将 KeyboardInterrupt 的返回值更改为 '\x03',以纪念你的查询(因为这样代码总是返回一个字符串)。 - mheyman
1
如何调整上述代码,以便返回一个元组来表示复杂按键,例如“Page Up”或“Left Arrow”? - Derek
1
我为了你,Derek,更新了代码。那是一个好问题。我在返回字符串和返回元组之间反复权衡,最终选择了元组,因为在我的看法中,这样做可以使使用结果的其他代码更清晰。哦,答案是在第一个字符后打开非阻塞并读取任何其他字符。如果你打字速度很快,你可能会欺骗它... - mheyman
显示剩余2条评论

48

如果您可以接受依赖于系统命令,您可以使用:

from __future__ import print_function
import os
import platform

if platform.system() == "Windows":
    os.system("pause")
else:
    os.system("/bin/bash -c 'read -s -n 1 -p \"Press any key to continue...\"'")
    print()

已经验证在Windows、Linux和Mac OS X上可以与Python 2和3一起使用。


如果你想一直运行直到收到一个信号(比如SIGINT),你也可以检查system的返回值,然后调用sys.exit(0) - James Taylor
@CarlosE Linux的变种可能也适用于OSX。如果适用,请告诉我。 - CrouZ
1
@CarlosE 我在 MAC OS X 10.7 上测试过,使用 Python 3.7.6 和 Python 2.7.1 都可以工作。你是怎么运行它的?根据这个问题,你需要从终端运行它:https://stackoverflow.com/questions/58986403/mac-os-os-systemcommand-display-nothing - CrouZ
sh: 1: read: 非法选项 -s - Mehdi
1
@Mehdi:看起来默认的shell(sh)不支持我的read命令。我已经编辑了我的答案,以便始终使用bash。这对你解决问题有帮助吗? - CrouZ
显示剩余4条评论

42

简单使用

input("Press Enter to continue...")

在使用Python 2时,会导致以下错误:

SyntaxError: 在解析时预期文件末尾(EOF)。

让代码可以同时在Python 2和Python 3上工作的简单修复方法是使用:

try:
    input("Press enter to continue")
except SyntaxError:
    pass

7
在Python 2中,请勿使用input函数,而应该使用raw_input函数。在Python 2中,input等价于eval(raw_input()) - Blorgbeard
4
除非用户按下回车键,否则此代码会忽略用户按下的所有按键,这与 OP 所询问的情况相当不同。 - Jonathan Hartley
3
如果您打算使用“input”,那么捕获SyntaxError是不合适的。无论用户输入什么都会被计算,因此如果例如他们键入了“1/0”,则会引发ZeroDivisionError而不是SyntaxError,并且您的程序将退出。 - Jonathan Hartley
1
正如@Blorgbeard所提到的,只需使用raw_input("按Enter键继续...")即可。我现在在调试时经常使用它。 - alltrue

30

跨平台,Python 2/3 代码:

# import sys, os

def wait_key():
    ''' Wait for a key press on the console and return it. '''
    result = None
    if os.name == 'nt':
        import msvcrt
        result = msvcrt.getwch()
    else:
        import termios
        fd = sys.stdin.fileno()

        oldterm = termios.tcgetattr(fd)
        newattr = termios.tcgetattr(fd)
        newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
        termios.tcsetattr(fd, termios.TCSANOW, newattr)

        try:
            result = sys.stdin.read(1)
        except IOError:
            pass
        finally:
            termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)

    return result

我移除了fctl/non-blocking的内容,因为它会导致IOError,并且我不需要它。我使用这段代码是因为我想要它阻塞。

附加说明:

我在PyPI上实现了这个包,其中包含很多其他好东西,叫做console:

>>> from console.utils import wait_key

>>> wait_key()
'h'

@Benoit 用哪个操作系统? - Gringo Suave
Linux - Ubuntu 20.04 - Benoit
可以在我的电脑上运行,你有将其导入吗? - Gringo Suave
在Windows上,您的函数返回一个字节字符串。 - q-l-p
我明白了,我想我会使用 getwch() 代替。#15 - Gringo Suave

19

Python 手册中提供了以下内容:

import termios, fcntl, sys, os
fd = sys.stdin.fileno()

oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)

oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

try:
    while 1:
        try:
            c = sys.stdin.read(1)
            print "Got character", repr(c)
        except IOError: pass
finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

这可以被应用到你的使用场景中。


16
最好的做法是复制你要链接的内容,这样即使链接失效(它们确实会失效!),知识也能保留下来。 - Richard
1
我该如何在Python 3.x中使它正常工作? 在3.x中,将打印语句更改为兼容后,这只是无限循环,并且不等待输入。 尽管在Python 2中很好用。 - cat
1
链接已更新以重定向到不同的页面。新链接在这里 - Matthias
在Python3中,通过稍微超过2to3的方式重写print语句,可以使其正常工作:if c: print(f"Got character {repr(c)}")。目前为止,Python 3 FAQ上还没有相关内容。 - PeterK

15

我不知道有没有跨平台的方法可以做到,但在Windows下,如果你使用msvcrt模块,你可以使用它的getch函数:

import msvcrt
c = msvcrt.getch()
print 'you entered', c

mscvcrt还包括非阻塞的kbhit()函数,可用于检查是否按下键,而无需等待(不确定是否有相应的curses函数)。 在UNIX下,有curses包,但不确定是否可以在不将其用于所有屏幕输出的情况下使用它。 这段代码在UNIX下运行:

import curses
stdscr = curses.initscr()
c = stdscr.getch()
print 'you entered', chr(c)
curses.endwin()

请注意,curses.getch()返回所按键的序号,因此为了使其输出与原来相同,我必须对其进行类型转换。


使用curses比手册中描述的相当复杂的示例要好得多,即使它涉及到一个巨大的依赖关系。+1 - Damian

5
您可以使用 keyboard 库:
import keyboard
keyboard.wait('space')
print('space was pressed, continuing...')

4
在Linux中,这需要root权限。 - RH6
在MacOS中也需要root权限。 - Ernest S Kirubakaran
在MacOS中也需要root权限。 - undefined

5
我刚开始学习Python,一度认为自己太笨,无法实现这里提供的最简单建议。然而,事实上,有一个需要了解的陷阱:
当从IDLE执行Python脚本时,一些IO命令似乎会表现完全不同(因为实际上没有终端窗口)。
例如,msvcrt.getch是非阻塞的,并且始终返回$ ff。这个问题早在很久以前就已经报告过了(参见例如https://bugs.python.org/issue9290),并且被标记为已修复,但是在当前版本的Python / IDLE中,问题似乎仍然存在。
因此,如果您发现以上任何代码无法正常工作,请尝试手动运行脚本,而不是从IDLE运行

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接