Таймер на выбор действия

Скажу сразу, у меня Windows 10 и Python 3.9, если эта информация вдруг понадобится.

Я никогда не сталкивалась с подобной задачей и использованием времени в программе, потому будет очень здорово, если на вопрос будет ответ, который можно будет доступно понять.

Мне хотелось бы, чтобы было установлено опрелённое время, которое довалось бы человеку на выбор нужного варианта ответа. Но. Нужен таймер, который в режиме реального времени будет отсчитывать время, чтобы человек мог его контролировать (максимум 10 секунд).

f = None
while f not in ['1' , '2']:
    f = input ("""Хотите взять предмет с собой?
1. Да. 
2. Нет. """)
    if f == '1':
        print ("Хороший выбор.")
    elif f == '2':
        print ("Очень зря.")
    else: 
        print ("Такого варианта ответа нет,ответьте на вопрос ещё раз.")

Ответы (2 шт):

Автор решения: GrAnd

Если под Windows, то можно привлечь msvcrt читать посимвольно в отдельном потоке. Хотя... В данном случае тут потоки даже не нужны.

import msvcrt, sys, time

def timed_input(text, string_mode=True, default=None, timeout=5):
    def flushed_output(out_text):
        sys.stdout.write(out_text)
        sys.stdout.flush()
    
    flushed_output(text)
    result = default
    input_text = ""
    if not string_mode:
        last_timeout = timeout
        time_text = str(timeout)
        time_text_len = len(time_text)
        flushed_output(f"[{time_text:>{time_text_len}}] ")
    time_start = time.time()
    while True:
        if msvcrt.kbhit():
            chr = msvcrt.getwch()
            flushed_output(chr)
            if string_mode:
                if ord(chr) == 13:
                    break
                input_text += chr
            else:
                input_text = chr
                break
        time.sleep(0.05)
        dt = int(time.time() - time_start)
        if not string_mode:
            if timeout - dt < last_timeout:
                last_timeout = timeout - dt
                flushed_output("\b"*(time_text_len+3)+f"[{last_timeout:>{time_text_len}}] ")
        if dt >= timeout:
            break

    if len(input_text) > 0:
        result = input_text
    flushed_output("\n")
    return result


name = timed_input("Введите ваше имя за 5 секунд: ", default="Иванушка-дурачок", timeout=5)
print("Добро пожаловать,", name)
f = None
while f not in ['1' , '2']:
    f = timed_input ("""Хотите взять предмет с собой?
  1. Да. 
  2. Нет.
Ваш выбор? """, string_mode=False, timeout=10)
    if f == '1':
        print ("Хороший выбор.")
    elif f == '2':
        print ("Очень зря.")
    elif not f:
        print ("Вы так и не определились, попробуйте снова.")
    else: 
        print ("Такого варианта ответа нет, ответьте на вопрос ещё раз.")
→ Ссылка
Автор решения: eri

А вот коротко, кроссплатфоменно, без внешних библиотек:

import threading
import signal

class Timeout(threading.Thread):
    def __init__(self, timeout):
        self.timeout = timeout
        self.done = threading.Lock()
        self.main_id = threading.get_ident()
        super().__init__()
        
    def run(self):
        ok = self.done.acquire(blocking=True, timeout=self.timeout)
        if not ok:
            signal.pthread_kill(self.main_id, signal.SIGINT)
    
    def __enter__(self):
        self.done.acquire()
        self.start()
        return self
        
    def __exit__(self, type, value, traceback):
        self.done.release()
        return True
        

with Timeout(2):
    try:
        input('введите за 2 сек:')
    except KeyboardInterrupt:
        print('не успел')
    else:
        print('успел')

Тут таймаут вынесен в контекст менеджер, чтоб основной код не засорять. pthread_kill наверное единственный метод прервать стандартный input() - кто знает другие?

→ Ссылка