如何读取键盘输入?

137 投票
5 回答
536561 浏览
提问于 2025-04-16 14:15

我想在Python中从键盘读取数据。我试了这个代码:

nb = input('Choose a number')
print('Number%s \n' % (nb))

但是它不管是在Eclipse里还是在终端中都不工作,总是停在提问的地方。我可以输入一个数字,但之后就没有任何反应了。

你知道这是为什么吗?

5 个回答

35

非阻塞的多线程示例:

当我们在等待键盘输入时,使用 input() 函数会让程序停下来,这通常不是我们想要的(我们常常希望继续做其他事情)。下面是一个非常简单的多线程示例,用来展示如何在主程序运行的同时,随时读取键盘输入。我在我的 eRCaGuy_PyTerm 串口终端程序中使用了这个技巧,可以在这里找到(搜索代码中的 input()

这个方法是通过创建一个后台线程,不断调用 input(),并将接收到的数据放入一个队列中。

这样一来,主线程就可以自由地做任何事情,随时从第一个线程接收键盘输入的数据,只要队列中有数据。

1. 纯 Python 3 代码示例(没有注释):

import threading
import queue
import time

def read_kbd_input(inputQueue):
    print('Ready for keyboard input:')
    while (True):
        input_str = input()
        inputQueue.put(input_str)

def main():
    EXIT_COMMAND = "exit"
    inputQueue = queue.Queue()

    inputThread = threading.Thread(target=read_kbd_input, args=(inputQueue,), daemon=True)
    inputThread.start()

    while (True):
        if (inputQueue.qsize() > 0):
            input_str = inputQueue.get()
            print("input_str = {}".format(input_str))

            if (input_str == EXIT_COMMAND):
                print("Exiting serial terminal.")
                break
            
            # Insert your code here to do whatever you want with the input_str.

        # The rest of your program goes here.

        time.sleep(0.01) 
    print("End.")

if (__name__ == '__main__'): 
    main()

2. 上面的 Python 3 代码,带有详细的解释注释:

"""
read_keyboard_input.py

Gabriel Staples
www.ElectricRCAircraftGuy.com
14 Nov. 2018

References:
- https://pyserial.readthedocs.io/en/latest/pyserial_api.html
- *****https://www.tutorialspoint.com/python/python_multithreading.htm
- *****https://en.wikibooks.org/wiki/Python_Programming/Threading
- https://stackoverflow.com/questions/1607612/python-how-do-i-make-a-subclass-from-a-superclass
- https://docs.python.org/3/library/queue.html
- https://docs.python.org/3.7/library/threading.html

To install PySerial: `sudo python3 -m pip install pyserial`

To run this program: `python3 this_filename.py`

"""

import threading
import queue
import time

def read_kbd_input(inputQueue):
    print('Ready for keyboard input:')
    while (True):
        # Receive keyboard input from user.
        input_str = input()
        
        # Enqueue this input string.
        # Note: Lock not required here since we are only calling a single Queue method, not a sequence of them 
        # which would otherwise need to be treated as one atomic operation.
        inputQueue.put(input_str)

def main():

    EXIT_COMMAND = "exit" # Command to exit this program

    # The following threading lock is required only if you need to enforce atomic access to a chunk of multiple queue
    # method calls in a row.  Use this if you have such a need, as follows:
    # 1. Pass queueLock as an input parameter to whichever function requires it.
    # 2. Call queueLock.acquire() to obtain the lock.
    # 3. Do your series of queue calls which need to be treated as one big atomic operation, such as calling
    # inputQueue.qsize(), followed by inputQueue.put(), for example.
    # 4. Call queueLock.release() to release the lock.
    # queueLock = threading.Lock() 

    #Keyboard input queue to pass data from the thread reading the keyboard inputs to the main thread.
    inputQueue = queue.Queue()

    # Create & start a thread to read keyboard inputs.
    # Set daemon to True to auto-kill this thread when all other non-daemonic threads are exited. This is desired since
    # this thread has no cleanup to do, which would otherwise require a more graceful approach to clean up then exit.
    inputThread = threading.Thread(target=read_kbd_input, args=(inputQueue,), daemon=True)
    inputThread.start()

    # Main loop
    while (True):

        # Read keyboard inputs
        # Note: if this queue were being read in multiple places we would need to use the queueLock above to ensure
        # multi-method-call atomic access. Since this is the only place we are removing from the queue, however, in this
        # example program, no locks are required.
        if (inputQueue.qsize() > 0):
            input_str = inputQueue.get()
            print("input_str = {}".format(input_str))

            if (input_str == EXIT_COMMAND):
                print("Exiting serial terminal.")
                break # exit the while loop
            
            # Insert your code here to do whatever you want with the input_str.

        # The rest of your program goes here.

        # Sleep for a short time to prevent this thread from sucking up all of your CPU resources on your PC.
        time.sleep(0.01) 
    
    print("End.")

# If you run this Python file directly (ex: via `python3 this_filename.py`), do the following:
if (__name__ == '__main__'): 
    main()

示例输出:

$ python3 read_keyboard_input.py
准备接收键盘输入:
hey
input_str = hey
hello
input_str = hello
7000
input_str = 7000
exit
input_str = exit
正在退出串口终端。
结束。

Python 的队列库是线程安全的:

注意 Queue.put()Queue.get() 以及其他队列类的方法都是线程安全的!这与 C++ 标准模板库中的队列和其他容器不同!由于 Python 的队列类及其方法是线程安全的,这意味着它们实现了线程间操作所需的所有内部锁定机制,因此队列类中的每个函数调用都可以视为一个单一的、原子操作。请查看文档顶部的说明:https://docs.python.org/3/library/queue.html(强调部分):

队列模块实现了多生产者、多消费者的队列。在多线程编程中,当信息必须在多个线程之间安全交换时,它特别有用。该模块中的 Queue 类实现了所有所需的锁定机制

参考资料:

  1. https://pyserial.readthedocs.io/en/latest/pyserial_api.html
  2. *****https://www.tutorialspoint.com/python/python_multithreading.htm
  3. *****https://en.wikibooks.org/wiki/Python_Programming/Threading
  4. Python: 如何从超类创建子类?
  5. https://docs.python.org/3/library/queue.html
  6. https://docs.python.org/3.7/library/threading.html
  7. [我使用上述技术和代码的仓库] https://github.com/ElectricRCAircraftGuy/eRCaGuy_PyTerm

相关链接:

  1. [我的回答] PySerial 非阻塞读取循环
88

看起来你在这里混用了不同版本的Python(Python 2.x和Python 3.x)……基本上,这样写是对的:

nb = input('Choose a number: ')

问题在于,这个功能只在Python 3中支持。正如@sharpner所说,对于旧版本的Python(2.x),你需要使用raw_input这个函数:

nb = raw_input('Choose a number: ')

如果你想把输入转换成数字,那么你可以试试:

number = int(nb)

……不过你需要注意,这样做可能会引发一个错误:

try:
    number = int(nb)
except ValueError:
    print("Invalid number")

如果你想用格式化的方式打印这个数字,在Python 3中推荐使用str.format()

print("Number: {0}\n".format(number))

而不是:

print('Number %s \n' % (nb))

不过这两种方式(str.format()%)在Python 2.7和Python 3中都能用。

137

如果你使用的是

input('Enter your input:')

那就用这个。

如果你想得到一个数字值,只需要把它转换一下:

try:
    mode = int(input('Input:'))
except ValueError:
    print("Not a number")

如果你用的是Python 2,那就要用raw_input,而不是input

撰写回答