7
\$\begingroup\$

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.

Check out on REPL.it

# 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)
\$\endgroup\$
5
  • 2
    \$\begingroup\$ Unfortunately your question is off-topic as of now, as the code to be reviewed must be present in the question. Code behind links is considered non-reviewable. Please add the code you want reviewed in your question. Thanks! \$\endgroup\$
    – Phrancis
    Jun 26, 2016 at 20:43
  • \$\begingroup\$ I can't paste it into SE because it thinks my comments are Markdown.An edit would be appreciated. \$\endgroup\$
    – Aditya R
    Jun 26, 2016 at 21:04
  • \$\begingroup\$ Here you go. You can use the "Code Sample" (or { }) 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\$
    – Phrancis
    Jun 26, 2016 at 21:06
  • 2
    \$\begingroup\$ Does this code work? And you want to make it simpler? Or it doesn't work the way that you want and you want to make it work? Reading your question makes it sound more like it doesn't work the way that you want, which would be off-topic. \$\endgroup\$
    – mdfst13
    Jun 26, 2016 at 22:10
  • 1
    \$\begingroup\$ @mdfst13 There's a link to a working demo. \$\endgroup\$ Jun 27, 2016 at 7:35

2 Answers 2

7
\$\begingroup\$

There are many things here which could be improved. I will however only improve some of them.

  1. 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.

  2. Use the if __name__ == "__main__": module. It makes your code clearer and reusable.

  3. blanks does not have to be a list of dicts. It is clearer if it only holds the type
  4. '%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.
  5. if len(ans) == 0: is a unorthodox way to change if a string is empty. A more pythonic approach is if not ans.
  6. Do not use quit() a better approach is to make the ans into a while loop

     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.")
     blank['ans'] = ans
    
  7. 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?

  8. You capitalize the words on input. However when asking if the user liked the story you do not lower the words. When i write Y it tells me I did not like the story. The key here is .lower().

    if feedback.lower() == "y":
        print("Thanks!")
    
  9. Your commenting is good, however it could be improved by using doc strings.

  10. 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 following

    import
    
    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()
    
  11. 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)
\$\endgroup\$
1
  • \$\begingroup\$ Wow. Thanks for the very, very detailed answer. I'll use these tips to write more Pythonic code moving forward. \$\endgroup\$
    – Aditya R
    Jun 28, 2016 at 0:16
3
\$\begingroup\$

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)__."""))
\$\endgroup\$
2
  • \$\begingroup\$ Thanks! I really need to deep-dive into Regular Expressions. They seem like a painless solution to most problems :) \$\endgroup\$
    – Aditya R
    Jun 28, 2016 at 0:21
  • 1
    \$\begingroup\$ "Painless" might be a bit optimistic, but regular expressions are indeed useful in many situations. \$\endgroup\$ Jun 28, 2016 at 0:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.