📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

I Learned Python By Building These Projects - Tutorial for Beginners

Tech With Tim1:04:27

Transcription

If you're a beginner in Python and looking for some simple projects to practice your skills, you've come to the right place. In this video, I'll walk you through three unique Python projects that will help you apply the theory you've learned into real-world applications.

Not only will I guide you through everything line by line, but I'll also explain my thought process and how I've designed the software so you can understand how to do this on your own. I'll even provide some mini challenges for you to try coding something by yourself before looking at my solution.

This is great for those of you who want to spend maybe an hour or two writing Python code. You don't want it to be too intense, but you want to learn how to do something by yourself and apply that theory into a real-world project.

These three projects are ones I built when I was learning Python, so I know they're great for beginners. They are perfect for those of you who have learned concepts like if statements, for loops, and variables but are struggling to take that next step.

With that said, let's get into the three projects. There will be timestamps down below in case you want to skip forward to a different project.

The first project we'll work on is a trivia game. This will randomly select a certain number of questions, ask the user those questions, and keep track of their score.

The next project we'll build is a random password generator, where we'll generate a password based on the user's preferences, such as whether it should include uppercase characters, special characters, numbers, and the length of the password.

Lastly, we'll build a to-do list manager, which will save a to-do list for the user on their computer permanently. We can then mark items as completed or uncompleted. You'll see how it works, and it's interesting to see how to work with files in Python.

So those are the three projects, and you're going to learn a lot in each. If you want more challenges that are a little shorter and want to practice your Python skills further, consider checking out my free newsletter. This newsletter sends out tons of emails containing all kinds of practice questions, particularly in Python, as well as sample project ideas, tips, and advice from me.

To make it even sweeter for you, I've prepared an entire guide on all the ways you can make money from coding, and I'll give that to you for free if you sign up for the newsletter. Simply sign up from the link below, and I'll email it to you. Then you'll be subscribed and will receive all of those free challenges. Everything is completely free; you don't need to pay for anything. Just sign up from the link below.

With that said, let's get into project number one.

Let's begin with the first project, which is a trivia game. For this game, I want to have a set of questions, and obviously, for all of those questions, we need some type of answer. The idea is to randomly pick a bunch of these questions so it's not always the same every time you run the program. We'll give those to the user and then see how many they get correct.

Before I start coding any of this out, the first thing I like to do is come up with a little bit of a plan. So, I would ask you, before you listen to what I'm about to say, think about the different components we need for this application.

Now, it's relatively simple; there will just be maybe three or four steps we need to take. You can pause the video and try to figure it out on your own.

For me, when I first start thinking about this, I think we need a list of questions. The first thing we need to do is come up with some questions and then store the answers to those questions and have them associated.

After we have the questions, we need to randomly pick some because we don't want it to always be the same. So, we need to randomly pick questions, ask those to the user, check if they are correct, keep track of the score, and then tell the user their score.

I'm going quickly here because this is relatively simple, but the idea is that I've at least thought about what I'm going to do. I've written a little bit of a plan, so I have some steps on the things I need to do. Now, when I start coding, I'm not completely lost, and I at least know where to begin.

I might not know how to solve all of these yet, but I've broken it into some smaller sub-problems that are easier for me to tackle.

First things first, let's get a list of questions. I'm just going to copy these in because I don't want it to take too long for me to type them out, and I'll explain why I've gone with this format.

What I'm actually using here is something called a dictionary. A dictionary in Python is a key-value store. It allows you to have some kind of key; in this case, the key is my question, and the value is the answer.

The reason I'm doing this is so that I can quickly look up the answer to a particular question. If you've never seen a dictionary before, the way it works is you can do something like questions, and then in square brackets, key is equal to some kind of value, and this would set this key-value pair.

In our case, all of the keys are the questions, and all of the values are the answers. If I want to see what a particular answer is, I simply reference questions at the key like this, and this would give me whatever the answer is.

For example, if I had this question and I pasted it here in the key and just looked at it and printed it out, it would actually give me three because that's the value associated with this key.

Hopefully, that makes sense. I'm just using the dictionary as a teaching opportunity because it allows me to look up the answers to any of the questions very quickly without having to use messy lists.

Now that we have all of our questions, the next thing I need to do is store the answers. I've done that, so I have the list of questions and the answers. Now I need to randomly pick the questions.

To randomly pick the questions, we're going to need to do some kind of random generation. I'm going to bring in the random module. You may not have seen this before, but this can generate random numbers for you and do a lot of random operations.

I'm going to import that at the top of my program. Now, I'm going to define a function, and this function is where most of the code for my game is going to live. This way, I can run the function again if I want to continually run the game multiple times.

I'm going to make a function and call it python_trivia_game. If you've not seen a function before, essentially this is a reusable block of code. I can just print something like "Hello," and now that I've defined this function, I can call it.

You define a function using the "def" keyword, write the name of the function, put a set of parentheses, put a colon, and then anything indented belongs to this function. It can be reused multiple times.

The way you use the function is you simply call it. I can just write the name of the function, python_trivia_game, and then call it using a set of parentheses. It will execute anything inside of it.

If I go here and run my code, you can see that I have "Hello." Obviously, I should mention that in order for this tutorial to work, you do need Python installed, and I imagine that you would have created a file to put this code inside of.

So, we have this function, and we can start running it by calling it down here. Now, obviously, inside of the function, we're going to move on to the next step, which is to randomly pick the questions.

To randomly pick the questions, I can do the following. First, I'm just going to get a list of what these questions actually are. I'm going to say questions_list is equal to a list of questions.keys().

When you're dealing with a dictionary, if you want to get all of the keys, you can use .keys(). If you want to get all of the values, you can use .values().

So, I'm just getting all of my keys. Now, I'm going to randomly select a certain number of these keys. I'm going to make a variable and say total_questions is equal to five; that's the number of questions I want to select.

While we're at it, we'll make another variable called score and set that to zero because later on, we're going to keep track of the score.

Now that I know the total number of questions and I have a list of all my questions, I can randomly select them. I'm going to make a variable called selected_questions, and this is going to be equal to random.sample.

I'll explain this in one second. We're going to pass our questions_list and the total_questions. You can read this; if I highlight over it, it chooses K unique random elements from a population sequence.

All that means is it can take in something like a list, and it will select K values that are unique from that list. That's all we want. We have our list of questions, and we want to select five of them.

So, random.sample, we just take five of them and give it to us in a list. Before we go any further, let's just print out what these selected questions are and make sure this part of the code is working.

Let's go up here and run our code. We're going to say python project1.py, and you can see that we have one, two, three, four, five questions. Perfect; that's exactly what I wanted.

Now that we have these questions, we need to ask them. What I'm going to do is loop through these questions using a for loop. A for loop is something that will repeat a certain number of times. In this case, we want to loop over our list.

To loop over our list, there are a few different ways we can do this, but I want to have access to the index inside of our list. The index is like the position of the elements: index zero, index one, index two, index three.

I want to have access to what the question actually is, so I'm going to use something you may not have seen before, which is called enumerate. Bear with me; I'll explain it in one second.

I'm going to say for idx, question in enumerate and I'm going to enumerate the selected_questions. What this is going to do is loop through all of the questions and give me the position or the index of each question and what the question value is.

If we have a list like this, let's just say a, b, c, then zero is the index for value a, one is the index for value b, and two is the index for value c.

So, what enumerate will do is give us the index as well as the value. If we were enumerating over this list a, b, c, then we would get zero for the index and a for the question, then we would get one for the index and b for the question.

Since we're doing that over selected_questions, then we'll get the index and whatever the questions are contained inside of that list. Hopefully, that makes sense.

Now we want to ask the question. To do that, I want to print out what the number of the question is and then ask it. I'm just going to do a print statement and use something called an f-string.

An f-string is simply a string that allows you to embed variables inside of it. I can put f and then a string. This can be an uppercase F or lowercase f; it doesn't matter.

Inside of here, anytime I want to print out the value of a variable, I can just put it inside a set of curly braces. If I want to print out the question number, I can do something like idx + 1 and then a dot.

What this is going to do is take my index and simply add one to it. It means if it's zero, it becomes one; if it's one, it becomes two. Then I'm going to put a dot and print out whatever the question is.

The reason I'm adding the plus one is that we're going to start iterating at index zero, and I don't want to say question zero; I just want to start by saying question one.

So, that's why I'm adding the plus one. Then we can ask the user for their answer, but for now, we're just going to start with that and see if that works.

Let's scroll up here and run our code again. You can see that it now prints out all of the different questions: 1, 2, 3, 4, 5. Great!

We're making good progress. We've now printed out all of the questions, effectively asking them to the user. Now, what we need to do is ask the user to give us their answer.

I'm going to make a variable called user_answer. Notice for all of my variables here that I'm trying to give them a meaningful name—something that makes sense.

Even if someone didn't know how to code, they'd be able to read it. Whenever you're writing your program, try to make sure everything reads like English.

In this case, we're defining that we have a Python trivia game, we're getting the list of questions, here's the total number of questions, and here are the ones that are selected.

Even if you didn't know how to code, you can still kind of understand what's going on here because you can read the variable names. Just a tip: get in the habit of doing that early; it's going to help you a lot later on in your coding journey.

For the user answer, we're going to use input. Input allows the user to start typing something in the terminal. All we're going to do is say "Your answer: " with a colon, and then I'm going to add a space.

The reason I add a space is that I want to add some separation from the colon. When the user starts typing, they're not squished with the colon; they have a little bit of space to start typing after that.

That's how input works; after you put this, the user can start typing on the same line, so we just add a space for a little bit of separation.

Then what we're going to do is convert whatever the user types in to lowercase. By default, whatever the user types will be given to us as a string data type in Python, and we'll just convert it to lowercase to ensure that if they accidentally type something in uppercase, we don't tell them it's wrong when we compare it to our answers.

Notice all of our answers are in lowercase, so we just convert what they type to lowercase. We're also going to add this .strip.

What strip is going to do is remove any spaces from the beginning or the end of the string. If the user accidentally typed something like " space 8," we don't want to tell them they're incorrect if the answer is actually eight.

So, we just remove that space so that when we're comparing it to the answer, we get the same thing. Hopefully, that makes sense, but strip is a good thing to use on user input.

That's going to ask the user for their answer, and we can quickly test that to make sure it works. Let's go up here and run our code. It asks us for the answer, and we can type some stuff in here to make sure that's working.

Okay, perfect. Very good. Let's close that and continue.

Now that we're able to get the user answer, we want to compare that to whatever the answer actually is. So, I need to get that answer first.

I'm going to say the correct_answer is equal to questions, and we're going to grab the question. We have our question, which is one of the keys in our questions dictionary.

We look in questions, use the question as the key, and that will give us whatever the answer is associated with that. Now we have the correct answer, and we can compare the user's answer to the correct answer.

To do that, we can use a simple if statement. We can say if user_answer is equal to the correct_answer. We'll just convert the correct_answer to lowercase here to ensure that everything is in lowercase, so we're not comparing cases.

If they got this correct, which this statement would tell us they did, we can print out "Correct," and we're just going to print a backslash n character.

This backslash n character will simply move us down to the next line in the terminal, so we get a bit of spacing between the next question we ask.

Now, as well as telling them they got it correct, we can increment the score. We can say score += 1. We have the score variable, and if they got something correct, we'll just add one to this.

That's how you can add one to a variable: score += 1.

Now, otherwise, if they didn't get this correct, we need to tell them they got it wrong. We're going to do a simple print statement, make this an f-string, and say "Wrong."

Then we will say the correct answer is, and inside a set of braces here, we're going to say correct_answer. We're just going to tell them what the answer is so they actually know.

Then I will put a period and add a backslash n character here again just so that we go down to the next line, so we have some spacing between the next question.

I know we wrote a good amount of code there. Just to quickly recap: we get the user's answer, we then get whatever the correct answer is, we compare these, and if they are the same, that means they got it correct.

So we say "Correct" and add one to the score. Otherwise, we tell them they got it wrong and what the correct answer was so they know.

Let's try this code before we go any further; we're actually almost finished, by the way. Let's start answering this.

How do you start a for loop in Python? "For."

What is the keyword to define a function in Python? Let's just get it wrong. Let's go "F," and it says "Wrong; the correct answer is 'def.'"

What data type is used to store true or false values? "Bull." Wrong; the answer is "Boolean."

Okay, so maybe we want to change that one, but that's fine. What is 10 over 3? "3."

Okay, perfect. And let's do this one: "Input." There we go.

All right, so that kind of completes the trivia game. However, we want to tell the user what their score was and keep track of the score. That's the next step here; we need to tell them what the score actually was.

Let's go down here and start by printing something out to tell them what their score was. We're going to make an f-string and say "Game over; your final score is," and then we can say score over total_questions.

We're just telling them, you know, like "4 out of 5," "5 out of 5," whatever it is. We just tell them how many questions they actually had.

Perfect, so that's fine. If I come here, let's give this a quick test and answer these quickly.

What is the keyword to define a function? That's "def."

To import a module is "import."

What is the result of 10 over 3? This is "3."

How do you start a for loop? Let's get one wrong, and it says your final score is "4 out of 5."

Fantastic! So there you go; that's pretty much it for this simple trivia game.

Again, the purpose of this was just to show you the thought process of how I created a little bit of a plan. I went through the plan step by step.

Obviously, we skipped a few steps, but generally, we followed this so we knew exactly where to start, and it was a lot easier for us to create this program because I knew the steps I wanted to follow.

I didn't have to stop constantly and keep thinking about what to do next.

Generally, what to take away from here is when you are building out some kind of project, always start by asking yourself, "What do I actually need to do? How can I break this down into smaller steps that are easier for me to solve?"

Then solve them one by one and combine them together to ultimately come up with the end code.

I'm sure some of this code might be a little confusing, especially if you are a complete beginner, but I wanted to not focus on the theory and just get directly into the project so you can see exactly how to write this.

If you want to analyze the code for yourself, I will leave it linked in the description.

Now we're going to move over to the next project, where we're going to build a random password generator.

All right, so we're moving to project number two, which is going to be our random password generator.

Now, same thing as before, I want to begin by coming up with a basic plan. My idea here is that I want to generate a random password, but I want this to be based on the user's preferences.

We can come up with whatever preferences we want, but usually, when you have a password, there are a few criteria that you need to fulfill.

For example, it may need to be a certain length, so we want to ask the user what length the password should be. It may have to contain special characters, uppercase characters, or numbers.

So, right away, the first thing I want to start writing down is what I need from the user before I can continue—like the user input that I want to collect.

The first step in this project is to collect user preferences, and those preferences are going to be the following: the length of the password, whether it should contain uppercase letters, whether it should contain special characters, and whether it should contain numbers or digits.

This is just what I came up with; you can change this to anything you want, and I would actually encourage you to do that—to mess around with it a little bit.

You can see length, contain uppercase, special, and digits. That's the first thing we need to do: collect that from the user.

Now, after this, it becomes a little less clear what we need to do, but I'm going to walk you through my thought process.

If we want to randomly generate a password, we're going to need to randomly select characters from the available characters that we could pick.

What I mean by available characters is that if the user doesn't want any uppercase, special, or digits, then we just have the lowercase characters to pick from, right?

We need to randomly select them and add them into some type of string. But if they selected all of these or whatever combination they have, that creates a pool of characters that we can then pick from.

So, what I'm going to do next is create all available characters or get all available characters so we know which ones we're able to use.

Once we know all of the characters that we can use, we can simply pick them one by one until we reach the desired length.

However, the only thing we're not really accounting for is that we want to ensure that when we do this random selection, if the user specified to include digits, special, or uppercase, we have at least one uppercase, one special, and one digit.

If we just randomly pick out of all of the characters that we have available to us, it's possible that we don't get one of those characters.

So, we need to add a caveat here and say ensure we have at least one of each character type.

Now, that also tells me, "What if the user asks for a password of length one or length two, but they say they want uppercase, special, and digits?"

That's not going to be possible because if they only want two characters, then I can't satisfy that constraint of having an uppercase character, a special character, and a digit.

So, I'm going to add another thing here and say ensure length is valid. We're just going to make sure the length of the password matches whatever criteria they've specified so we can actually generate what they're asking for.

Now, there are some other things we probably need to do here, but generally speaking, this is a decent enough plan where I've thought through what I need to do, and I can start coding this out.

Hopefully, that's clear. What I'm going to do here is import two modules to begin: random and string.

Now, you may not have seen the string module before, but this gives us access to a list of all the characters that are lowercase, uppercase, digits, or special characters.

It just saves us a little bit of time from having to manually write all of them out ourselves.

Next, we're going to create a function like we did before, and notice by naming it generate_password, I'm just making it super clear what this function actually does.

Now, the first thing we had on our list is we need to collect the user preferences, like the length and whether it should contain uppercase, special, or digits.

So, let's start writing that out. We're going to say length is equal to input, and for the input, we'll say "Enter the desired password length: " like that.

Then what I'm going to do is just do .strip to remove any spaces.

Now, after that, I'm going to have a variable, and I'm going to call this include_uppercase, and we're going to have an input that says "Include uppercase letters? (yes or no): ".

I'm just going to put inside a set of parentheses "yes or no" so we're telling the user what we expect them to type in.

Then, just like before, I'm going to strip this and convert it to lowercase so that when we compare what they typed in, we don't check for the case.

Now, we're just going to copy this and paste it two times because we're going to have the same thing again, except now rather than uppercase, we're going to have special and digits.

So, we're going to say include_special and change this to "Include special characters? (yes or no): ".

Then for digits, we're going to say include_digits and "Include digits? (yes or no): ".

Now we've collected the input from the user, and what I'm going to do is first convert this length to a number.

By default, when you type something in as input, the type of that variable is going to be a string, and I want my length to be a number because I need to have a numeric value so I can use it in Python.

For example, if it's a string, it's just not in the correct type, so I'm just going to put an int function around this input.strip.

What this does is it collects the user input, strips any spaces, and then converts it to an int.

Now, this is going to assume that we type in a valid integer. If you don't type in a valid integer, this will actually give you an error, but for our program, we're going to keep it simple and just assume that they're going to give us a valid number.

If they don't, the program is going to crash. So, for now, let's call the function and test this.

I don't like to write too much code without testing, so let's do that. Let's go here and then say python project2.py, and let's just go through these fields.

For length, let's say 10, include uppercase: yes, include special: no, include digits: yes. Very good!

Now we're able to enter all of these values. Sweet!

Now that we've done that, the next thing I want to do is ensure the length is valid.

I'm just going to make this really simple and ensure that they give us a length that's at minimum four because if they give us a length of four, then we know no matter what they've asked us to do here, we can include a character of every single type.

So, I'm going to say if the length they've given us is less than four, then I'm going to print "The password length must be at least four characters," and I'm simply going to return from the function.

Now, when you return, you're just going to exit the function and go back to the line that called it, which means anything I put down here will not run.

So, if this is true, we will simply return, which means anything beneath this line won't happen inside of the function.

So, just exiting out early.

Great! Now that we've done that, the next step is to get all of the available characters.

How do we do that? Well, I'm going to show you. You probably haven't seen this before.

I'm going to say lowercase is equal to string.ascii_lowercase. What ascii_lowercase will do is give me all of the lowercase letters.

That's all it does; it's just going to give me a string that contains all of these lowercase letters.

If we want to print it out just to test our sanity here, we can do that.

So, let's just go here and run the code. Oops, it says "invalid literal for int with base 10."

That's what I was talking about, so let's actually fix this because now it's going to tell us that the password must be a minimum of four.

Okay, five, and then go here, and you can see that it gives us all of the lowercase letters. Sorry about that mess there, guys, just because of the way I was running the code.

But the point is, lowercase = string.ascii_lowercase gives us all the lowercase letters, which is what we want.

Next, we're going to get uppercase, and this is going to be string.ascii_uppercase.

However, we're only going to get this if include_uppercase; otherwise, we're going to have an empty string.

So, pretty much what I'll say is if include_uppercase is equal to "yes," then we will get all of these; otherwise, we will just have an empty string.

Now, you may not have seen this Python code before; that's intentional. I'm trying to make it a little bit more complex to hopefully teach you something.

But this is known as an inline if statement or a ternary statement.

Anyways, the name's not important; the point is the thing on the left-hand side will be the value in this variable if this condition is true; otherwise, it will be the thing after the else.

So, it'll either be an empty string or it will be all of the uppercase characters.

Hopefully, that's clear. Now, let's copy that and do the same thing for special.

I'm going to say special is equal to string.punctuation if include_special is "yes."

Then we can copy it one more time and do digits.

We're going to say digits is equal to string.digits if include_digits is "yes."

Now that we've got all of the characters that we should be using, what we're going to do is combine them all into one large string.

I'm going to say all_characters is equal to lowercase + uppercase + special + digits.

This is known as string concatenation, and it just squishes the strings together.

For example, if you add "hello" and "world" to this, then what you would get is "hello world."

It would just combine them together.

Hopefully, that makes sense, but that's what we're doing here.

The reason why this will work is that if we're not including any of these, then it will just be an empty string.

When you add an empty string to another string, it just doesn't do anything; it just gives you the original string.

So, if we are including these, then we will combine them together; if we're not, then we'll just have an empty string.

So, let's do another sanity check here and print the all_characters to make sure this works based on the parameters we give it.

I'm going to run this. I'm going to say length 10, let's include uppercase, let's not include special, and let's include digits.

You can see that we get everything except the special characters.

Let's run it again. Let's go 10, let's not include uppercase, but let's include special and digits.

Now you can see we get all of the special characters as well as the digits and, of course, the lowercase letters, which we always have.

Now that we've selected essentially the pool of characters that we're able to use, the next thing we need to do is randomly pick characters up to the length.

We also need to ensure that we have at least one of each character type.

Now, we can do this in different orders, but what I'm going to do is first just select one character from each category that we need to have at least one of.

In order to ensure that we have at least one special character, one digit, or one uppercase letter, we're just going to start by selecting one of those characters if that's included, and then we'll have those be a part of our finished string.

The idea is if you want to have an uppercase character, we'll start by randomly picking one uppercase character.

Then we'll randomly pick, for example, a special character if you want to have that.

We'll randomly pick a digit if you want to have that, and that will cover three characters of whatever the password length will be.

Then we will just pick whatever the remaining number of characters are.

So, let's say the length is 10, and we've already picked three; we'll then just pick seven random characters from all characters and put those in the string.

This will just ensure that we always have at least one of whatever the user asked for.

So, in order to do this, we're going to have this list called required_characters.

What I'm going to do is just add into this list all of the characters that we need to have.

They'll be at max three, so I'm going to say if include_uppercase is equal to "yes," then what I'm going to do is say required_characters.append, and we're going to randomly select a character from the uppercase list.

To do that, we're going to say uppercase, and we need to randomly pick one of the characters inside of here.

Now, there are different ways to do this. For example, we can use random.choice.

So, I can say random.choice and then uppercase, and this will just randomly pick one character from uppercase, and in fact, that's exactly what we're going to do.

Now, we're going to do the same thing. We're going to say if include_special is equal to "yes," then we're going to say required_characters.append random.choice on special.

Then you guessed it; we're going to do the same thing for digits.

So, if include_digits is equal to "yes," then we're going to say required_characters.append random.choice, and this will be the digits.

Again, the idea is if we're including these—special, digits, uppercase—then we're just going to randomly pick one character from just that list of uppercase, special, or digits.

We're going to add that into required_characters, and this will give those characters that we need to have no matter what.

Then we'll select the rest that we're going to have randomly.

In order to do this, we're now going to say that the remaining length or the remaining number of characters that we need to generate are going to be equal to the length minus the length of our required_characters.

So, if we selected three required characters, then the remaining number of characters we need to pick is whatever the length of the password will be minus however many we've already picked.

Then we're going to say our current password is equal to the required_characters, and we're going to start adding more characters into this password.

All I'm doing is just renaming this now, so I'm saying, "Okay, we've picked these required characters; these will be in our password that we're going to randomly generate."

Now we're just going to start picking new random characters to add inside of this password.

Notice that I'm doing this as a list. The reason why I'm using a list right now is that I can just keep appending values inside of it very easily, and then later I can take this list and convert it into a string.

I can shuffle around the values inside of it; it's just a bit more flexible if I'm going to keep randomly picking a bunch of new items.

There's also a speed concern here; it's faster to add items into a list than it is to keep creating a new string.

I don't want to get into that too much in this video.

What I'm going to do now is loop through the remaining characters, the remaining length that I have, and just pick new characters.

I'm going to say for underscore in range and then remaining_length.

What this is going to do is have this for loop running however many characters are left to pick.

So, if the remaining length is seven, then this for loop is going to run seven times.

Now, the underscore is simply a placeholder variable when you don't want to define something.

Typically, I would do something like for idx in range remaining_length, and then idx would be equal to 0, 1, 2, 3, 4, and it would count up to whatever this value is but not include it.

But I'm not going to use this variable; I don't care what the index actually is. I just want this for loop to run seven times or remaining_length times, so I can just use an underscore as a placeholder.

That's really all it is; it's known as an anonymous variable.

Now that I have that here, what I'm going to do is randomly pick a value from my characters.

I'm going to say my_character is equal to random.choice, and I'm going to pick something from my all_characters.

So, all of the characters that are valid to choose from, I'm just going to pick one, and then I'm simply going to add that to my password.

So, I'm going to say password.append(my_character).

Okay, so that now is going to give me a character.

What I'm going to do is print the password so that we can see what this looks like before we convert it into a string.

Let me run the code quickly. I'm going to say let's go 10, let's include everything.

Okay, and you can see that this is the password that was generated.

Now, notice that we have an uppercase letter, we have a special, and we have a digit, and it comes in this order.

The reason why is because we started by creating or selecting those three required characters, then we just picked random characters, so we got whatever was randomly selected.

So, that's fine, but I want to now shuffle this up and make it even more random because I don't want the three characters that I selected to always be at the beginning of the list or the beginning of the password.

So, what I'm going to do is have another randomization where I'm going to say random.shuffle(password).

Now, what random.shuffle is going to do is look at this list and randomly mix up all of the items that are inside of it.

That's it; it's just going to mix them all up.

Now that it's done that, what I can do again is print out the password.

Keep in mind it's still a list; it's not yet a string, so we'll convert it to that in one second.

Let's just have a look. So, let's make this, for example, length four.

Let's go yes, yes, and yes.

There you go; we get our password.

If we ran this again and did the same thing, we would get a different result.

So, I just want to convert this to a string.

In order to convert it to a string, I can say my_string_password is equal to an empty string.join(password).

Now, the way that this works is join will take a list—in this case, we have a list of different characters—and combine all of the elements in that list together using whatever this string is as a separator.

In this case, we have an empty string, which just means combine all of the values in a list to a string.

But if we did something like a pipe, then it would combine all of them with a pipe in between every single character or a comma in between every single character.

It's a very useful method to know in Python.

So, what we'll do is simply return the string_password, and then what we're going to do down here is say password is equal to generate_password, and we're just going to print out what the randomly generated password was.

Okay, so that's the code. Let's test this, and then we can kind of go through it and just review what we've written.

So, we're going to enter the length; let's make it like 25.

Let's just go yes, yes, or I guess that's not going to include now, so we'll say yes.

And then there we go; we get our password.

Let's test it again. Let's go maybe 10, let's go yes, no, no, and there we go; we get our password.

Okay, so you can mess around with this, and you can keep getting a bunch of random passwords, but that is this project.

To quickly recap, we had to start by collecting all of the user values or preferences, make sure the length is valid, and then we get all of the characters that we can possibly use.

Then we generate all of the required characters that we need to have in this password.

We then get the remaining length after we've generated those, and then we start generating the password by just randomly selecting characters from all of the valid ones that we can pick.

Then we shuffle up whatever we've selected, convert it to a string, and return it back to the user.

When we return something from a function, it simply gives it back to wherever the function was called.

So, string_password now gets returned to this and gets stored in this password variable, and then we can print out the password.

Okay, so hopefully you enjoyed that. That was project two.

Let's move on to the next one.

We are moving on to project number three, which is going to be a to-do list management app.

Now, my idea behind this is I want to store to-do list items for a user in a permanent area where, if the user runs the application again, they can still see those same items.

Yes, you probably wouldn't use this in the real world, but I'm just showing you something so you can understand how to work with files and various data types and load and save information so you can use it when the program has finished running.

Anyways, the idea is we're going to have some kind of to-do list items, like "to-do list item one," and then we can mark it complete or incomplete.

Then we want to be able to save it to the disk and load it from the disk.

So, let's start writing some of these features. We have a little bit of a plan now.

First things first, we're going to have to load existing data, right? When we run the program, we need to load existing items.

Then we're going to have a few different operations we should be able to perform.

Now, the first operation might be creating a new item. Then maybe we want the ability to list items to see which ones already exist, and we probably want the ability to mark an item as complete.

Okay, and then lastly, we probably need to be able to save items.

I'm not coming up with a full detailed plan, but I'm at least thinking of some of the different operations that I'm going to need to perform so I can start kind of stubbing out the program and just understanding what it is that I'm about to do.

So, we have create, list, mark, and save.

Now, what I want to do is just write some of the functions that we'll have in order to perform these operations.

The way that I start a larger program like this is I begin by just writing out the core operations. I don't implement them, so I don't write all of the code and make it work, but I just write the names of the different functions and start mapping the program out a little bit.

So, visually, I know what's going on.

Like we said, we need to load existing items, so I'm going to make a function called load_tasks.

Now, I'm just going to say pass. When I say pass, this just means this function is empty right now, and I'll come back to it later.

Next, we're going to have save_tasks.

We'll say pass, and then we're going to have a few more.

So, we'll have view_tasks, and we're going to have create_task.

Then we're going to have define mark_task_complete.

Okay, perfect. So, those are kind of the five main things that I'm going to need to be able to do.

Then I'm going to have one function, and I'm going to call this main.

The main function is typically the entry point to your code, like the first thing that you start running.

In this main function, what I'm going to do is just connect some of these functions together.

I'm going to have the user type in maybe 1, 2, 3, 4, or 5, depending on the type of operation they want to perform, and then I'll call these different functions.

You'll see what I mean, but hopefully, this is making a little bit of sense already in terms of the different things that we're going to need to do.

We'll talk about saving into a file and all of that kind of stuff.

For now, inside of my main function, I'm going to start by just loading my tasks.

Even though I don't have this written yet, I'm going to say tasks is equal to load_tasks.

You can see that I'm kind of mapping out the program on what I want to happen, and then later on, I'll go and implement all of these functions.

So, we're going to load the tasks; that's the first thing we need to do.

Then I'm going to set up a loop, and this loop is going to continually ask the user what I want them to do.

I'm going to say while True, we're going to use a while loop. This means we're just going to keep running until we eventually decide to break out of the loop.

Then here, I'm going to have a print statement, and I'm just going to do a backslash n and say "To-do list manager."

I'm just kind of telling them what the application is.

Then I'm going to print out the different operations that they'll be able to perform.

I'm going to say "1 is view tasks," going to say "2 is maybe add task," and then "3 is complete task."

Okay, and then lastly, let's do "4 is exit."

So, these are the main things that they'll be able to do.

Then I'm going to ask the user what they want to do.

I'm going to say choice is equal to input, and I'm going to say "Enter your choice: " and then I'm just going to strip this.

So, we're going to go .strip to remove any of the spaces, and then I'm going to see what they typed in.

Essentially, what I'm going to do is say if they typed 1, then we're going to view the tasks.

If they typed 2, then I'm going to, sorry, where is this? Create the task or add the task.

If they typed 3, then I'm going to mark the task as complete.

I'm just kind of mapping this up and connecting the different functions together so the flow is there, and then we can implement them one by one.

So, I'm going to say if choice is equal to 1, then what I want to do is say view_tasks.

Now, I'm going to say elif choice is equal to 2, and the way that the elif works is if this is not true, then we will go to check this condition.

So, we're going to say elif choice is equal to 2, then we're going to say add_task.

Okay, or what do we call it? We called it create_task.

Then we're going to say elif choice is equal to 3, then we're going to mark the task as complete.

Then we're going to say elif choice is equal to 4, then we're going to print "Goodbye," and we are simply going to break.

When we break, that's going to exit this loop, which will then end the program.

Lastly, we'll have an else and say "Invalid choice; please try again."

So, if elif, elif, elif, else.

If this is not the case, we check this; if it's not the case, we check this; if it's not the case, we check this; and if none of these were the case, then we go into the else.

Okay, and that's pretty much it.

Then what we can do is call the main function.

So, again, I know I keep repeating this, but what I've done is just mapped this out.

I've connected the different functions, and I now have an idea of what it is that I need to do.

Really, all that's left is to write these individual functions, and it's significantly easier for me to think about these one at a time than it is for me to try to write them all at the very beginning.

So, let's just run the code for right now and see if this works.

We're going to see nothing really is going to be happening, but if I run project3.py, you'll see the printout: "To-do list manager, view tasks, add task," etc.

If I type 5, I get "Invalid choice."

If I type 1, then it just goes to the next thing, right?

3, and then 4, and then I exit.

Okay, so just testing that so far is working.

All right, so that is great.

We now have the options and kind of like what we're able to do, and the next step is to start writing these out.

So, let's begin by worrying about how we're going to load the tasks in and how we're going to save them into a file.

Now, I'm just going to remove this for right now because we don't really need the plan anymore.

I'm going to import a module called json.

Now, JSON stands for JavaScript Object Notation, and you can create JSON files that can store data that looks very similar to a Python dictionary.

What we're going to be doing is having a Python dictionary.

We're going to have tasks, and then in this tasks list, there will be a list here.

We're going to have all of the individual tasks, like "task is this," and then we're going to have a variable that tells us if the task is completed or not.

So, the idea is we can store this in a JSON file because what we've written here is what's known as JavaScript Object Notation—again, something very similar to what a Python dictionary looks like.

Then we can just store this file, update if this is completed or not completed, add additional tasks to the list, or remove tasks if we want, and we'll just store that in a file.

We'll load it in, modify the tasks, and then override the file or save the new changes to the file.

Just bear with me as I write out the code, and you'll understand how it works.

So, first things first, I'm going to say my_file_name is equal to "to_do_list.json."

Okay, now we can just manually make a JSON file just so you can kind of see how this works to begin.

I'm going to make a new file in my editor, and I'm going to make sure this file is in the same directory where my Python code is.

So, where my project3 file is is where I'm going to make this file, and I'm going to call it "to_do_list.json."

Now, if you're working in VS Code, you'll see that it gives you the braces here indicating that this is JSON.

What we need to do is write what looks like a Python dictionary.

So, these open braces, we need to specify a key. In this case, the key that I'm going to use is just going to be called "tasks," and then we can just have a simple empty list.

So, for right now, we have no tasks, so we just say "tasks" is equal to an empty list.

Okay, this is our JSON file.

Now, what I'm going to show you how to do is load in this JSON file and then save content to the JSON file.

So, the way it works is we can go in load_tasks, and we can write the following:

We can say with open, we're going to specify the name of the file, which is our file name, and we're going to specify the mode we want to open this in.

Now, "r" is the mode for reading, so if we want to read the file and not modify it, we open it in "r" mode, which is what we're doing right here.

Then we say as file, and all we're going to do is return json.load(file).

What this does is it opens the file for us, creates this file object, and then we use this JSON module to load the file as a Python dictionary.

So, what's going to be returned to us is simply a Python dictionary that looks exactly like this.

That's it; that's all we get.

Now, what we're going to do is just add a little bit of error handling here because it's possible the file may not be found.

So, we're going to say try, and then we're going to put all of this inside of here, and we're going to say except.

For the except, we are simply going to return an empty list.

The reason we're doing this is if some kind of error occurs while we attempt to do this, like loading the file, then we'll just return an empty list indicating that, hey, we don't have any tasks.

Actually, rather than doing that, we're just going to return exactly what it would look like.

So, we're going to say tasks like this because that's what's going to be returned from our JSON.

So, we want to make sure we return an object that's in the same format as this one.

Okay, hopefully, that's clear.

Again, we're trying to open the file, and we load the JSON using json.load.

It just gives us the Python dictionary version of this file, and then if something goes wrong with this, we're just going to return an empty list of tasks.

Perfect!

So, for now, let's just try to call this function.

So, we have tasks equal to load_tasks, and let's just print what the tasks are.

Just for sanity, I'm just going to add inside of here a random string that just says "Hello, world," so we can see if we get that.

Okay, so I'm going to go here and run, and notice it tells me we have tasks: "Hello, world."

If I change this and add another task, let me just get out of this and run again, you see that now we get the other task.

So, we're loading from this file into our Python script.

Great! So, that's how we load.

Now that we've loaded, what we want to do is learn how to save.

So, in order to save our tasks, we're going to take as a parameter to this function the task that we want to save.

Now, this is just a variable that we can pass to the function, and then the function can use this to save the tasks.

So, in order to save this, we're going to do a very similar thing to what we did when we were loading.

We're going to just copy all of this, and we're going to change the width.

So, we're going to say with open file_name, this time we're going to open this in "w" mode.

Now, "w" mode stands for write mode, and this will override an existing file.

So, if this file already exists, it's essentially going to delete it and then recreate it and write inside of it whatever we tell it to write.

So, rather than returning json.load, we're going to say json.dump, and we're going to dump the tasks into the file.

What json.dump does is it takes our Python dictionary and just writes it into the file.

That's it; that's all that's happening—very straightforward.

Then for our except, we're just going to have a print here and say "Failed to save."

So, if again some error occurred here, then we'll just say that we failed to save the tasks.

So, just again for a sanity check, let's check and see if this works.

So, I'm just going to copy this, and I'm going to go down here, and before I load the tasks, I'm just going to save some.

I'm going to say save_tasks, and let's just load this, and we'll say "Saved task."

Okay, so we're passing in what we want to save, which is a valid Python dictionary, and then we should load it and print it out to see if it works.

Okay, so let's try this.

I'm going to go to 4, and then let me just clear this and rerun, and you can see that it shows us the saved task, which is what we just added.

If we look at the file, we now have our saved task, which is what we saved from this line right here.

Okay, let me get out of this.

All right, so now we know how to save and load our various tasks.

So, now we can get rid of this, and we'll just load them in.

The next thing we need to do is have the ability to view our tasks.

Also, actually, let's create one first.

Before we can view them, we should be able to create them.

So, for our create_task, we're actually going to take in the tasks that exist, and the reason for that is that we're going to add to these tasks the task that we just made.

So, you'll see how this works in one second, but we're going to say create_task, and we're going to first get the description of the task.

So, let's say description is equal to input, and we'll say "Enter the task description: " like that.

Okay, and then same thing; we'll just strip out any of the leading or trailing white spaces.

Now, we're just going to make sure that they did give us a description.

So, we're going to say if they did give us something—so if we have some kind of description, that's what this is checking—then we're going to say tasks.append.

However, this really is going to be tasks, and then tasks.append, and I'll explain why we're doing that in one second.

We're going to append to this a description, and the description will be this, and we're going to say "complete" is equal to false.

Okay, now what I'm doing is I'm saying, "All right, we're going to pass our tasks."

Now, the task is going to be a dictionary that looks like this: we're going to have tasks and then a list of all of the tasks.

So, what I'm going to do is get the tasks list from this key, and that's going to give me this, and then I'm going to append to that list this new task.

So, I'm just adding it inside of here.

Then once I do that, since I've now updated my tasks, I can save them.

I'm going to say save_tasks and then tasks.

Okay, so we pass it there, and then automatically saves it in the file for us.

Then we can just have a simple message, and we can print and say "Task added."

Perfect!

Then we'll have an else statement that says "Description cannot be empty."

Okay, perfect.

Now, one thing to note here is that when we pass this dictionary to this function, we're actually able to modify the dictionary, and any changes that are made inside of this function will exist outside of the function.

So, later on, when we start passing tasks to these various functions, any update that we've made will be existing inside of this main function as well.

Now, this has to do with the property of dictionaries in Python known as mutability, which means they can be modified once they've been created.

I don't want to talk too much about it because it's a whole other video and a long explanation, but essentially what you need to know is that dictionaries are mutable.

So, if you pass them between different functions, which is what we're doing here, any changes that you make to them will apply outside of the function as well.

Essentially, to the object, no matter where it was passed, any of those changes will exist on it.

So, here when I add this new task to it, I don't need to return this back to the function; it will just automatically be updated for me.

So, the next time I use it in this function, it will have those changes applied.

All right, hopefully, that makes sense, but this now gives us the ability to create a new task.

What I need to do, though, is go to my create_task function and pass this tasks variable to it because it requires this parameter.

We need to pass that, so we pass it to the function, we ask for the description, if they did give us a description, we then add this new task, we save it, and then we say "Task added."

Otherwise, we just say that it cannot be empty.

So, let me just remove what I have for right now inside of the tasks so it's an empty list, and let's try to create a new task.

So, I'm going to run this.

I'm going to go to 2 to add a task.

Now it says "Enter the task description," and we're going to go "Item list one."

Okay, then it says "Task added."

Now, to check if that works, we can simply go here, and you can see that now we have this new task where we have the description and if it's completed or not.

Perfect!

Okay, so now that we've added the tasks, we probably should have the ability to view the tasks.

So, to do that, I'm going to add the view_tasks function here, and I'm going to pass my tasks to it.

Then I'm going to go to my view_tasks function, and I'm going to take tasks as a parameter because in order to view the tasks, I need to know what they are.

Then I'm going to print them out.

So, first things first, I'm just going to check if we do actually have any tasks.

So, I'm going to say if the length of tasks is equal to zero, then I'm going to print "No tasks to display."

Otherwise, I'll go ahead and display them.

Now, one thing I'm going to do to make the code a little bit simpler is I'm going to say task_list is equal to tasks.

Then I'm going to replace this with the task_list because I'm going to be using this list a few times.

So, I'm just going to reference what the list is, so now I don't need to keep writing tasks.tasks like that.

Okay, so if the length of the task_list is zero, then I'm going to say "No tasks to display."

Otherwise, I will display them.

So, to display them, I'm going to have a simple print and say "Your to-do list: " like that.

Then I will start printing them out.

Similar to before, I'm going to say for idx, task in enumerate, and I'm going to enumerate over my task_list.

Then I'm simply going to print out the tasks.

So, for my task, I have a key which is a description and another key which is if it's complete.

So, I want to look at the key that tells me if the task is completed or not so I can mark if the task is completed or if it's pending.

So, I'm going to say my_status is equal to "Completed" if the task["complete"] is true; otherwise, it's going to say "Pending."

In a set of square brackets, okay?

So, similar to what I showed you in the other projects, we say if this thing here is true, we grab this string; otherwise, we grab this string.

Then what I can do is print out my task.

So, I can put an f-string, and I can put a set of braces.

I can say idx + 1, so idx plus one, so that I get the number of the task.

I'll say dot, then I will reference the task["description"] like this.

Okay, and I'm just going to make sure that my quotation marks are different here.

So, if you have double quotes for your f-string, then use single quotes when you're referencing the key for the task; otherwise, sometimes you can get some errors here.

So, again, single quotes inside of the string and double quotes to define the string.

Then I am going to say the status, and I'll just do a pipe and then status.

Okay, so what this will do now is it will tell me the number of the task, it will say the description of the task, and then it will tell me whether the task is completed or whether it is pending.

All right, so I'm curious to see if this is going to work.

So, let's run the code.

Let's go view tasks, and you can see that it says, "Hey, I have item list one, and this is pending."

Perfect!

And maybe I want to add a bit more space, so actually, I'm going to go ahead and do that.

I'm going to leave, and then I'm just going to add a backslash n here.

Let's just print an empty line just so that we have a bit more space when we view our tasks.

Okay, so let me run this again, and now we will say view tasks, and it says, "Your to-do list: one item list one pending."

Perfect!

Okay, and then I'm going to quit, and actually, let's just test it one more time.

Let's add another task.

So, let's go, you know, "Make my bed" or something, and "Task added."

Let's view the tasks, and you can see now that we have two tasks.

Sweet!

Okay, let's get out of that.

All right, so now that we have the ability to view the task, create the task, and save and load them, the last thing we need to do is mark them as complete.

Now, in order to mark them as complete, we're going to have to view what the tasks are.

Then we're going to have to ask the user to enter the task number, and based on the task number they input, we're going to mark that as complete.

So, first things first, if they ask us to mark it as complete, we're just going to print out the tasks.

So, we're going to say view_tasks, and we're going to take tasks as a parameter here and print them out.

Then we're going to ask the user to tell us the number of the task they want to mark as complete.

So, we're going to say task_number is equal to input, and we're going to say "Enter the task number to mark as complete: ".

Okay, then we're going to convert this; we're going to do .strip, and we're going to convert this to a number because we need the integer.

All right, now we're going to make sure that they give us a valid number.

So, we're going to say if 1 <= task_number <= len(tasks), then this means that it's valid; otherwise, we're going to print "Invalid task number."

Okay, so if they gave us a valid number, what we need to do is mark the task as completed.

So, we're going to say tasks[task_number - 1]["complete"] is equal to true.

All right, I know that's a little bit of code.

What I'm doing is I'm looking at my task, which remember is a dictionary.

I'm grabbing the list of tasks and then referencing the individual task in there, which is whatever the number they gave me minus one.

The reason for that is when we print out the tasks, the first task we show is task number one, but really its index in the list is zero.

So, we need to subtract one from whatever the user gives us, then we access the "complete" key.

Again, you can see that we have this inside of the task, and we just mark it as true.

All right, then we're going to say save_tasks, and we're going to pass our tasks, and we're going to print "Task marked as complete."

Okay, perfect!

All right, one thing I will do, though, is I will just add a try and except here because it's possible that we could get an error.

So, I'm going to put all of this inside of the try.

Try, and then I'm just going to accept, and I'm going to say "Enter a valid number."

So, if this gives us an error, this line right here, then rather than crashing the program, we're just going to tell them, "Hey, this is invalid."

So, we'll say "Enter a valid number."

All right, so that should be able to mark our task as completed.

Now, let's go ahead and run this and try.

So, we have our tasks.

Let's go complete task, and we got an issue.

It says "Mark task complete missing one positional argument: tasks."

So, the reason we got that error is because mark_task_complete doesn't pass the tasks, which we need.

So, if I pass that here, now that should fix it.

If we come back up here and run again, and I go 3, now it shows us the list.

It says "Enter the task number to mark as complete."

Let's do 1, and then let's view our task, and you can see now that it says that this one is completed.

Amazing!

So, that's pretty much it for our to-do list application.

In fact, that's all the code that I'm going to write, but I will give you a challenge if you want to continue here and ask you if you can make it so you can mark a task as incomplete and if you can delete tasks.

Those are two operations you should be able to add here with the code that I've shown you so far.

Give that a shot and let me know how that goes.

Anyways, guys, that is going to wrap up this video.

All of this code will be available from the link in the description.

If you enjoyed, make sure to leave a like, subscribe, and I will see you in the next one.

[Music]