Python 2.7 curses中keypad()与SIGINT的异常行为问询
嘿,我来帮你把这两个Python 2.7 curses场景下的getch()与SIGINT交互细节掰明白——毕竟老版本的curses信号处理确实有不少容易让人困惑的地方!
场景1:需要3次按键才终止的行为拆解
先假设你的代码大概是这种连续调用getch()的典型测试结构:
import curses def main(stdscr): stdscr.addstr("First getch:\n") stdscr.refresh() key1 = stdscr.getch() # 第一次调用 stdscr.addstr(f"Got: {key1}\n") stdscr.addstr("Second getch:\n") stdscr.refresh() key2 = stdscr.getch() # 第二次调用 stdscr.addstr(f"Got: {key2}\n") stdscr.addstr("Third getch:\n") stdscr.refresh() key3 = stdscr.getch() # 第三次调用 if __name__ == "__main__": curses.wrapper(main)
当你按下**第一次按键(Ctrl+C)**时,第一个getch()正处于阻塞等待输入的状态。默认情况下,Python 2.7的curses并不会立即中断这个阻塞并返回-1——因为系统终端的信号处理机制会暂时把SIGINT挂起,直到终端有实际的输入字符进入缓冲。
于是你需要按第二次按键(任意普通键),此时终端缓冲被激活,第一个getch()返回这个普通键的ASCII码(比如按a就返回97)。程序走到第二个getch()时,之前挂起的SIGINT终于被处理,这个getch()直接返回-1。但因为代码还有第三个getch()调用,你得按第三次按键才能让程序走完流程终止。
这就是为什么总共需要三次按键操作的核心原因。
场景2:仅需2次输入就终止的行为拆解
这种情况通常是因为你调整了信号处理逻辑,或者代码结构更简洁,比如这样:
import curses import signal # 让curses接管SIGINT处理,而非触发Python默认的KeyboardInterrupt signal.signal(signal.SIGINT, signal.SIG_IGN) def main(stdscr): stdscr.addstr("First getch:\n") stdscr.refresh() key1 = stdscr.getch() # 第一次调用 stdscr.addstr(f"Got: {key1}\n") stdscr.addstr("Second getch:\n") stdscr.refresh() key2 = stdscr.getch() # 第二次调用 if __name__ == "__main__": curses.wrapper(main)
当你按下第一次按键(Ctrl+C),因为我们设置了忽略SIGINT(交给curses内部处理),getch()在收到信号时会立即中断阻塞状态,直接返回-1——这就是你看到输出里马上出现-1的原因。
之后程序走到第二个getch(),你按第二次按键(任意键),程序完成最后一次输入调用后就终止了,所以总共只需要两次操作。
本质上,两种场景的差异核心在于SIGINT的处理时机和curses与系统终端的交互逻辑:默认情况下信号会被挂起直到有实际输入唤醒缓冲,而调整信号处理后,curses会直接将信号转化为getch()的-1返回值,无需额外的唤醒按键。
内容的提问来源于stack exchange,提问作者PrashantKumar96

