Делаю библиотеку для создания текстовых приключенческих игр.
Вот мой код 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
Я попытался сделать параметры функции одной строкой, и я не могу придумать что-нибудь еще, чтобы попробовать.
Вы можете ожидать, что пользователь библиотеки передаст вызываемый объект, который будет вызываться в случае выбора этой опции:
...
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'))()
, тогда сообщение будет напечатано. Вы также можете добавлять аргументы к лямбда-выражениям, исследовать их самостоятельно.