Providing advice based on user's favourite YouTuber
I've heard of dictionary lookups but I don't really understand what they are so if anyone thinks that they maybe relevant and could show me how to implement it that would be great!


Here you go!

Use Dictionaries!

The entire if/elif/else if yelling, if not screaming, to be put into a dictionary. This is a lot faster than your implementation because it's a simple key lookup, instead of multiple logical checks to test the user input.

Here is your test function (now named favorite_youtuber):

def favorite_youtuber():

    youtube_response = {
        'dr. phil': 'You are an awesome lad!',
        'james charles': 'Errmm. Ok...',
        'bloamz': 'Ok then.',
        'ali a': 'I mean. Thats old but ok...',
        'jacksepticeye': 'Thats kinda cool',
        'will smith': 'Thats diferent. I rate that.',
        'jack black': 'You have good taste.',
        'jack white': 'I like him as well.',
        'dr. mike': 'So you like learning then!',
        'morgz': 'I mean just leave now.'
    }
    # Since some youtubers in the list below are not included in the dictionary, I left the list. #
    my_favorite_youtubers = ['Dr. Phil', 'Mr. Beast', 'T-Series', 'PewDiePie', '5 Minute Crafts', 'The Ellen Show']

    youtuber = input('Enter your favourite youtuber:  ').lower()

    if youtuber in youtube_response:
        print(youtube_response[youtuber])
    else:
        print('I dont know that one. Ill check them out.')

    print(f'My favourite youtuber is {random.choice(my_favorite_youtubers)}!')


A dictionary works by utilizing keys and values, like so:

my_dict = {
    "key": "value of any type",
    ...
}


In this case, the key is the name of the youtuber that the user inputs, and the value is the response. This prevents you from having to have multiple print statements depending on what the user inputs. Now, all you have to do is make sure that the youtuber entered by the user is included in the dictionary's keys, utilizing this line:

if youtuber in youtube_response:


Format Your Strings!

The era of my_string = a + " " + b is over. You can now format your strings to include your variables directly in them! Take a look:

print(f'My favourite youtuber is {random.choice(my_favorite_youtubers)}!')


Essentially, the value of the code within {} is placed in that position in the string.

Another option is to use .format(), which is a method called on a string. Take a look:

print('My favourite youtuber is {}'.format(random.choice(my_favorite_youtubers)))


They both do the same thing. It's up to you which one you want to use.

.lower() utilization

Instead of

maybe = input("Y/N ")
maybe = maybe.lower()


do this

maybe = input("Y/N ").lower()


Since input() returns a string, .lower() applies to that string. This prevents you from having to write that extra line, and it makes your code a little nicer.

\n

Instead of

print("That is not a valid option")
print("")


do this

print("That is not a valid option.\n")


It adds a newline character at the end of the string, doing exactly what you're doing but in a nicer way.

Repetitive User Input

Now let's talk about your try_again function.

There's a lot to break down here. I find it easier to show you my improved version of your code, and walking you through what I did. Have a look:

def run_app():
    while True:
        favorite_youtuber()
        again = input("Play again? (Y/N)").lower()
        while again not in "yn":
            print("Please enter Y/N!")
            again = input("Play again? (Y/N)").lower()
        if again == "n":
            break
    print("Thank you for using this app!")


It's fairly self explanatory. The one thing I want to talk about is the nested while loop.

Instead of checking if something is within a tuple ("y", "n"), you can check if something is within a string "yn". It's easier to understand this way. The while loop keeps asking for input until the user enters a "y" or a "n". This is easier than having nested while True: loops, as those can get very messy very fast.

Since you only want to see if they don't want to keep playing, you only need to check for the existence of an "n". Then, it's a simple break statement to print out the final goodbye.

Main Guard

Last thing I'm commenting on.

You should use a main guard when running this program. Why?

Let's say you want to import this module into another program, because you don't want to rewrite all this code in a different file. When you import the module, that spare try_again is going to run. That is not what you want. Containing this extra code in a main guard will prevent this from happening. It's a simple if statement:

if __name__ == "__main__":
    run_app()


I renamed your try_again to run_app(), since that name is more fitting of what the program is doing.