Функции, работающие в списке в качестве параметра

Делаю библиотеку для создания текстовых приключенческих игр.

Вот мой код main.py:

from txtadvlib import utils

utils.menu("Main Menu", ["play","about","quit"])
utils.getInput(prompt = "user>",
               ifs=[
                 "1",
                 "2",
                 "3"
               ],
               thens=[
                 print(1),
                 print(2),
               ],
               catch = "print('caught')"
              )

Вот код, который я использую для библиотеки:

import os
import time
import random
import colorama
from datetime import date
from colorama import Fore

class utils:
  def menu(title,optionsArray,):
    print(title)
    cycles = 0
    for i in optionsArray:
      cycles += 1
      print(f"[{cycles}].{i}")
      
  def getInput(prompt, ifs, thens, catch):
    choice = input(prompt)
    for i in ifs:
      if choice == i:
        eval(thens[ifs.index(i)])
        break
      else:
        eval(catch)

Я не хочу использовать eval для каждой функции, так как это требует от пользователя библиотеки форматирования любых функций в виде строк.

Вот проблема: функции в списке thens запускаются сразу, а затем идет ввод.

Вот результат:

Main Menu
[1].play
[2].about
[3].quit
1    <--- function in the list ran
2    <--- function in the list ran
user>   <--- input prompt

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
67
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы можете ожидать, что пользователь библиотеки передаст вызываемый объект, который будет вызываться в случае выбора этой опции:

...
import functools 

def catch():
  print('caught')

utils.getInput(prompt = "user>",
               ifs=[
                 "1",
                 "2",
                 "3"
               ],
               thens=[
                 lambda: print(1),
                 functools.partial(print, 2),
               ],
               catch=catch
              )

Тогда ваш код должен выглядеть примерно так:

...
  def getInput(prompt, ifs, thens, catch):
    choice = input(prompt)
    for i in ifs:
      if choice == i:
        thens[ifs.index(i)]()
        break
      else:
        catch()

Вам нужен другой способ передать список thens, потому что, как вы выяснили, такие вызовы, как print(1), выполняются сразу.

Один из способов сделать это — передать кортеж объекта функции и его аргументов, которые будут вызваны позже:

thens = [
    (print, 1),
    (print, 2)
]

Поскольку за именем функции print не следуют скобки (), она не вызывается сразу.

functools.partial это еще один способ сделать это.

Основная идея состоит в том, чтобы использовать функции как объекты, к счастью, Python может легко это сделать. Например. вы можете использовать def, чтобы создать функцию и передать ее в меню.

def on_play():
    print(1)

def on_about():
    print(2)

def on_catch():
    print('caught')

utils.getInput(prompt = "user>",
               ifs=["1","2","3"],
               thens=[on_play,on_about,],
               catch=on_catch)

Важно знать, что как только вы ставите круглые скобки рядом с именем функции, функция вызывается. Вы хотите избежать этого и вызвать его позже, поэтому опускайте круглые скобки при определении thens. В тот момент, когда вы будете готовы вызвать функцию, добавьте круглые скобки к объекту функции, хранящемуся в thens, например. thens[i]()

def getInput(prompt, ifs, thens, catch):
    choice = input(prompt)
    try:
        i = ifs.index(choice)
        thens[i]() # now call
    except:
        catch() # now call

Иногда вам нужно было бы передавать аргументы функциям, чтобы использовать их повторно. Мне нравится использовать functools.partial для этого. Он принимает один существующий функциональный объект и любые аргументы и создает новый функциональный объект, в котором некоторые аргументы заполнены и исключены из исходного списка аргументов. Вы можете добиться этого, чтобы убедиться, что thens[i]() можно вызывать без каких-либо аргументов, потому что вновь созданный объект функции через functools.partial уже заполнит все необходимые аргументы.

from functools import partial

def on_menu(mode, username=None, game_version=None):
    if mode == 'play'
        print(1, username)
    elif mode == 'about'
        print(2, game_version)

utils.getInput(prompt = "user>",
               ifs=["1","2","3"],
               thens=[
                   partial(on_menu, mode='play', username='Bob'),
                   partial(on_menu, mode='about', game_version='v1.0'),],
               catch=on_catch)

Вы также можете использовать лямбда-выражения Python для создания простых однострочных функций вместо написания длинных def.

utils.getInput(prompt = "user>",
               ifs=["1","2","3"],
               thens=[
                   lambda: print('Whatever'),
                   lambda: print('Game v1.0'),
               catch=on_catch)

По сути, этот оператор lambda: print('Whatever') также создает объект функции, который еще НЕ вызывается, это произойдет только тогда, когда вы сделаете (lambda: print('Whatever'))(), тогда сообщение будет напечатано. Вы также можете добавлять аргументы к лямбда-выражениям, исследовать их самостоятельно.

Другие вопросы по теме