I created this Mad Libs generator, but I don't know how to get the value of the key "ans" into a format string tuple. I feel like it's not Pythonic and that it's very convoluted and over-complicated.
# Our Mad Lib
madlib = "On the %s trip to %s, my %s friend and I decided to invent a game. Since this would be a rather %s trip, it would need to be a game with %s and %s. Using our %s to %s, we tried to get the %s next to us to play too, but they just %sed at us and %s away. After a few rounds, we thought the game could use some %s, so we turned on the %s and started %s to the %s that came on. This lasted for %s before I got %s and decided to %s. I'll never %s that trip, it was the %s road trip of my %s."
sourceURL = "http://marcelkupka.cz/wp-content/uploads/2013/03/mad.jpg"
# A list storing the blanks for the Mad Lib
blanks = [
{"suggestion": "adjective", "ans": ""},
{"suggestion": "place", "ans": ""},
{"suggestion": "adjective", "ans": ""},
{"suggestion": "adjective", "ans": ""},
{"suggestion": "noun, plural", "ans": ""},
{"suggestion": "noun, plural", "ans": ""},
{"suggestion": "noun", "ans": ""},
{"suggestion": "verb", "ans": ""},
{"suggestion": "noun", "ans": ""},
{"suggestion": "verb", "ans": ""},
{"suggestion": "action verb", "ans": ""},
{"suggestion": "noun, plural", "ans": ""},
{"suggestion": "noun", "ans": ""},
{"suggestion": "verb that ends in ing", "ans": ""},
{"suggestion": "noun", "ans": ""},
{"suggestion": "measurement of time", "ans": ""},
{"suggestion": "adjective", "ans": ""},
{"suggestion": "action verb", "ans": ""},
{"suggestion": "verb", "ans": ""},
{"suggestion": "adjective", "ans": ""},
{"suggestion": "noun, something you can own", "ans": ""}
]
print("Road Trip Mad Lib\nWhen the program asks you, please enter the appropriate word.")
print("There are %i blanks in this Mad Lib. " % (len(blanks)))
# Ask the user for each one
for blank in blanks:
ans = input(blank['suggestion'].capitalize() + "> ")
if len(ans) == 0:
print("Please don't leave anything blank. It kills the experience.")
quit()
blank['ans'] = ans
# The list that stores the format string
fs = []
# Get the answers from the blanks list
for dictionary in blanks:
fs.append(dictionary['ans'])
# Print the formatted Mad Lib
print(madlib % tuple(fs))
feedback = input("Pretty funny, right? [y/n] ")
if feedback == "y":
print("Thanks!")
else:
print(":( Sorry. I'll try better next time.")
print("\n" + "="*10 + "\nMad Lib sourced from " + sourceURL)
2 Answers 2
There are many things here which could be improved. I will however only improve some of them.
The golden standard for style in python is PEP 8. It explains in excruciating detail how to structure your code. I whole heartily recommend skimming through it and follow it.
Use the
if __name__ == "__main__":
module. It makes your code clearer and reusable.blanks
does not have to be a list of dicts. It is clearer if it only holds the type'%s %s' % ('one', 'two')
is the old string format while'{} {}'.format('one', 'two')
is the new one. I recommend sticking with the new one. Check here to learn more about Pythons awesome string formating options.if len(ans) == 0:
is a unorthodox way to change if a string is empty. A more pythonic approach isif not ans
.Do not use
quit()
a better approach is to make theans
into a while loopfor blank in blanks: ans = "" while not ans: ans = input(blank.capitalize() + "> ") if not ans: print("Please don't leave anything blank. It kills the experience.") blank['ans'] = ans
A bigger concern is that you update
blank['ans'] = ans
, but then immediately move the values into a list.fs = [] # Get the answers from the blanks list for dictionary in blanks: fs.append(dictionary['ans'])
Why not have the
answers
as a list from the start?You
capitalize
the words on input. However when asking if the user liked the story you do not lower the words. When i writeY
it tells me I did not like the story. The key here is.lower()
.if feedback.lower() == "y": print("Thanks!")
Your commenting is good, however it could be improved by using doc strings.
A more severe concern with your code is that it is not modular. You have everything lumped into a single file. Really you should NEVER have that. Create a function named
get_user_input
which handles all the errors. Another code to ask the user to play again and so forth. A basic structure is something like the followingimport CONSTANTS def some_function(): def another_function(): def main() while True: some_function() play_again = input('do you want to play again? [y/n]: ') if play_again.lower() not in ['y', 'yes', 'ok']: break if __name__ == '__main__': main()
In PEP 8, it is recommended that the maximum linewidth is 79 characters. This is a good rule of thumb. You can use triple quotation marks to format long strings. Here are some other methods to format long strings.
\${}{}\$
# Our Mad Lib
madlib =
'''
On the {} trip to {}, my {} friend and I decided to invent a game. Since
this would be a rather {} trip, it would need to be a game with {} and {}.
Using our {} to {}, we tried to get the {} next to us to play too, but they
just {}ed at us and {} away. After a few rounds, we thought the game could
use some {}, so we turned on the {} and started {} to the {} that came on.
This lasted for {} before I got {} and decided to {}. I'll never {} that trip,
it was the {} road trip of my {}.
'''
sourceURL = "http://marcelkupka.cz/wp-content/uploads/2013/03/mad.jpg"
# A list storing the blanks for the Mad Lib
blanks = [
"adjective",
"place",
"adjective",
"adjective",
"noun, plural",
"noun, plural",
"noun",
"verb",
"noun",
"verb",
"action verb",
"noun, plural",
"noun",
"verb that ends in ing",
"noun",
"measurement of time",
"adjective",
"action verb",
"verb",
"adjective",
"noun, something you can own"
]
print("Road Trip Mad Lib\nWhen the program asks you, please enter the appropriate word.")
print("There are %i blanks in this Mad Lib. " % (len(blanks)))
# Ask the user for each one
answers = []
for blank in blanks:
ans = ""
while not ans:
ans = input(blank.capitalize() + "> ")
if not ans:
print("Please don't leave anything blank. It kills the experience.")
answers.append(ans)
# The list that stores the format string
# Print the formatted Mad Lib
print(madlib.format(*answers))
feedback = input("Pretty funny, right? [y/n] ")
if feedback.lower() == "y":
print("Thanks!")
else:
print(":( Sorry. I'll try better next time.")
print("\n" + "="*10 + "\nMad Lib sourced from " + sourceURL)
-
\$\begingroup\$ Wow. Thanks for the very, very detailed answer. I'll use these tips to write more Pythonic code moving forward. \$\endgroup\$Aditya R– Aditya R2016年06月28日 00:16:13 +00:00Commented Jun 28, 2016 at 0:16
Which would you rather read:
madlib = "On the %s trip to %s, my %s friend and I ..." blanks = [ {"suggestion": "adjective", "ans": ""}, {"suggestion": "place", "ans": ""}, {"suggestion": "adjective", "ans": ""}, ... ]
... or
"""On the __(Adjective)__ trip to __(Place)__, my __(Adjective)__ friend and I ..."""
The first version, which is what you wrote, is unmaintainable. It's hard to see where the blanks
fit within the madlib
, and it is cumbersome to represent a story as two objects. The second version, on the other hand, looks just like the human-readable template in your image.
Here's one way to write a function that fills in the blanks, using regular expression replacements.
import re
def madlib(template):
"""
Given a template that contains blanks like __(Noun)__,
prompt the user to enter suggestions to fill in the blanks,
and return the completed story as a string.
"""
def fill_blank(match):
while True:
ans = input(match.group(1) + '> ')
if ans:
return ans
print("Please don't leave anything blank. It kills the experience.")
blank_re = re.compile('__\((.+?)\)__')
blank_count = len(blank_re.findall(template))
print("There are {} blanks in this Mad Lib.".format(blank_count))
return blank_re.sub(fill_blank, template)
print(madlib("""On the __(Adjective)__ trip to __(Place)__, my __(Adjective)__ friend and I decided to invent a game. Since this would be a rather __(Adjective)__ trip, it would need to be a game with __(Noun, plural)__ and __(Noun, plural)__. Using our __(Noun)__ to __(Verb)__, we tried to get the __(Noun)__ next to us to play too, but they just __(Verb)__ed at us and __(Action verb)__ away. After a few rounds, we thought the game could use some __(Noun, plural)__, so we turned on the __(Noun)__ and started __(Verb that ends in "ing")__ to the __(Noun)__ that came on. This lasted for __(Measurement of time)__ before I got __(Adjective)__ and decided to __(Action verb)__. I'll never __(Verb)__ that trip, it was the __(Adjective)__ road trip of my __(Possessive noun)__."""))
-
\$\begingroup\$ Thanks! I really need to deep-dive into Regular Expressions. They seem like a painless solution to most problems :) \$\endgroup\$Aditya R– Aditya R2016年06月28日 00:21:15 +00:00Commented Jun 28, 2016 at 0:21
-
1\$\begingroup\$ "Painless" might be a bit optimistic, but regular expressions are indeed useful in many situations. \$\endgroup\$200_success– 200_success2016年06月28日 00:22:56 +00:00Commented Jun 28, 2016 at 0:22
{ }
) button in the post editor to format your code after you paste it, so you know for next time. Hope you get good answers! \$\endgroup\$