0% found this document useful (0 votes)
124 views189 pages

Full Python Course

This document is a tutorial video on how to start coding with Python, including the installation of Python and an IDE, as well as hands-on projects. It covers basic programming concepts such as variables, data types, and type casting, providing examples and explanations throughout. The tutorial emphasizes practical coding exercises, encouraging viewers to engage with the material by creating their own variables and projects.

Uploaded by

edgarnepadaet
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
124 views189 pages

Full Python Course

This document is a tutorial video on how to start coding with Python, including the installation of Python and an IDE, as well as hands-on projects. It covers basic programming concepts such as variables, data types, and type casting, providing examples and explanations throughout. The tutorial emphasizes practical coding exercises, encouraging viewers to engage with the material by creating their own variables and projects.

Uploaded by

edgarnepadaet
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 189

What's up everybody?

In this video, I'm going to teach you everything you need to know to start coding
with Python. I've also included 20 different hands-on projects to help you learn. You can find the entire
list in the description of this video.

Our final project will be a weather app that fetches real-time weather data from an API. Even if you've
never coded anything in your life before, I'll walk you through the basics, the ABCs of programming. If
that sounds good to you, then I encourage you to sit back, relax, and enjoy the show.

This course doesn't cost you anything, but if you would like to help me out, you can help increase its
reach by hitting the like button, leave a random comment down below, and subscribe if you'd like to be
a fellow bro. Thank you. I appreciate it.

I don't like boring introductions, so I say we just jump right in. There's two things we'll need to
download. The first is a Python interpreter to convert our written code to machine code. We're going to
head to python.org, go to downloads, and download the latest version. We will open this executable.

If you're on Windows, you'll want to check this check box, **Add Python exe to PATH**, and we will
install now. The setup was successful. And that's all you need to do to download the Python interpreter.

The second download we'll need is an IDE, an integrated development environment. Basically, a place
where we can write code. For IDEs, there's two popular choices when writing Python code: PyCharm and
VS Code. If you already use VS Code, you can stick with that. Just be sure to download the Python
extension.

I find PyCharm more beginner friendly if you've never coded before. If you would like to use PyCharm,
go to jetbrains.com/pycharm. And we will click this green download button. There's two versions of
PyCharm, the professional version and the community version. The professional version is paid for. I
would not recommend using it only because there's plenty of free IDEs on the market.

We'll use the community edition, the free one, because I don't like to pay for things and I'm sure you
don't either. Select the correct download for your operating system. I'm running Windows. I will
download PyCharm. We will open this executable. Click next. You could select a destination folder. I'll
keep it as is. Next, I'll create a desktop shortcut, but you don't necessarily need to. Click next. Install.
And we'll just have to give it a moment.

Okay, the setup is now complete. I'll check this checkbox to run PyCharm when we close this window.

After opening PyCharm, we're going to create a new project. You can rename your Python project. I'll
keep it as is. You can select a location. Again, I won't change that. You can create a sample welcome
script, but for this tutorial, we won't. Let's select the latest Python version and create our new project.
In the menu to the left, we're going to create a new Python file. File > New > Python File. Let's name this
file **main**. But really you can name it anything and select Python file. Python files end with the `.py`
file extension. We should have our main Python file within our Python project folder.

Now we're going to print something to the console window. Within our main Python file, we're going to
write a print statement. So type `print` add a set of parentheses. Between the set of parentheses, we
will add a set of double quotes to print something or single quotes. Either one. My own preference is
double quotes.

Normally in a programming tutorial, the instructor would tell you to print something such as "hello
world," but we like to be different here. Instead, think of your favorite food. In this case, I like pizza. I
will print:

```python

print("I like pizza")

```

To run our Python program, we will click the screen arrow to run our main Python file. We should have a
console window that displays our output:

```

I like pizza

```

or whatever your favorite food is.

Let's print another line of code. Let's print:

```python

print("It's really good")

```

By adding a second print statement, we are printing a second line of code.


Now we'll discuss comments. The Python interpreter doesn't output comments. To write a comment,
you use a pound sign. I like to call this a hashtag. My comment will be:

```python

# This is my first Python program

```

Comments are used as notes for yourself or for other people reading this code. If I were to run this code
again, this comment is not displayed to the output. We still have:

```

I like pizza

It's really good

```

All right, everybody. So, that is your very first Python program. And in the next topic, we'll discuss
variables.

---

All right, everybody. We are moving on to variables. A variable is a container for a value. There's four
different data types. We'll discuss strings, integers, floats, and booleans. Yes, I know that's a silly name.

A variable behaves as if it was the value it contains. Each variable should have a unique name. Let's say
we have a variable of `first_name`. To assign a variable, you use the assignment operator of equals:

```python

first_name = "Bro"

```

For text, a string is a series of text. This can be double quotes or single quotes. My own preference is
double quotes. Why don't you type in your first name? This variable of `first_name` will behave as if it
was this value, this series of characters.

So to demonstrate this, I'm going to print my first name variable:


```python

print(first_name)

```

So place it within a print statement without quotes. That will print your first name. Now, you don't want
this within quotes because then you're literally printing the word `first_name`.

You could use your variable along with some text by using what is called an **F-string**. That's the
easiest way to display a variable. So, you type `f` then a set of quotes. The `f` means format. So, let's say
the word hello. Then we will add our variable. We will insert our variable into this text when using an F-
string. To do that, you need a set of curly braces. Then insert your variable. So the result is:

```python

print(f"Hello, {first_name}")

```

which outputs:

```

Hello, Bro

```

Let's create another variable. Let's say we have a variable of `food`:

```python

food = "pizza"

```

Let's print the following. You like add a placeholder. Again, I'm using an F-string:

```python

print(f"You like {food}")

```
Output:

```

You like pizza

```

Let's create an email. Use your own email or make up one. Let's say:

```python

email = "[email protected]"

print(f"Your email is {email}")

```

Output:

```

Your email is [email protected]

```

So these are strings. I'm going to add a comment that these are strings:

```python

# These are strings

```

A string is a series of characters. They can include numbers but we treat them as characters.

Now we have integers. An integer is a whole number. An example of this could be somebody's age. How
old are they? According to my YouTube statistics, many of you are between the ages of 18 through 24.
Let's say that I'm 25:

```python
age = 25

```

Your integer should not be within quotes because it would be a string. Then technically if I would like to
work with this variable again I'll use an F-string:

```python

print(f"You are {age} years old")

```

Output:

```

You are 25 years old

```

Another example of an integer could be a quantity. You are buying a certain amount of something.
Maybe I am buying three items. I wouldn't have half an item. This would be a float technically rather
than an integer. We are buying three of something. So let's print the following:

```python

quantity = 3

print(f"You are buying {quantity} items")

```

Output:

```

You are buying 3 items

```

Another example of an integer could be an amount of people. Let's say `num_of_students` like a
classroom. There are 30 students in our class:
```python

num_of_students = 30

print(f"Your class has {num_of_students} students")

```

Output:

```

Your class has 30 students

```

Those are integers. They're whole numbers. And again, make sure they're not within quotes because
then technically they would be a string. Integers we can use in arithmetic expressions. If they were
strings, we couldn't.

Then we have floats. Float means floating-point number. A float is a number, but it contains a decimal
portion. An example would be a price. What is the price of something? $10.99:

```python

price = 10.99

print(f"The price is ${price}")

```

Output:

```

The price is $10.99

```

Let's precede our placeholder with a unit of currency. I'll pick American dollars, but feel free to pick
something else.

So, floats contain a decimal portion.


What about a grade point average (GPA)? Let's say my GPA is 3.2:

```python

gpa = 3.2

print(f"Your GPA is {gpa}")

```

Output:

```

Your GPA is 3.2

```

What about a distance? A distance can contain a decimal portion. 5.5 kilometers maybe:

```python

distance = 5.5

print(f"You ran {distance} km")

```

Output:

```

You ran 5.5 km

```

Or you could add `mi` for miles, but I'll stick with kilometers.

Okay. Then we have booleans. A boolean is either `True` or `False`. Let's say we're a student:

```python

is_student = True
```

If we are a student, we could say that this is `True`. `True` starts with a capital T. If we weren't a student,
let's say we graduate, we could say that this is `False`. Again, the first letter is capital.

Booleans only have two options, `True` or `False`.

So, let's say that I am a student. Then I will print:

```python

print(f"Are you a student? {is_student}")

```

Output:

```

Are you a student? True

```

With boolean values, we really don't output them directly. You're more likely to see them used
internally within a program, such as when working with if statements. This is a topic we'll discuss in the
future, so don't worry.

You may see:

```python

if is_student:

print("You are a student")

else:

print("You are not a student")

```

Our variable of `is_student` is `True`. We will print the if statement:


```

You are a student

```

If this were `False`, we will print whatever is within else:

```

You are not a student

```

Let's think of a few more examples.

Is something for sale like a car or a product of some sort? Let's say that is `True`:

```python

for_sale = True

if for_sale:

print("That item is for sale")

else:

print("That item is not available")

```

`for_sale` is set to `True`. This variable is `True`. We will print:

```

That item is for sale

```

Else if it were `False` we print:


```

That item is not available

```

One more example. Let's say we have a boolean variable of `is_online`. Is somebody online? I will set
that to `True`:

```python

is_online = True

if is_online:

print("You are online")

else:

print("You are offline")

```

`is_online` is set to `True`. We will print:

```

You are online

```

Else if it were `False` we print:

```

You are offline

```

All right everybody, so those are variables. A variable is a reusable container for a value. There's four
basic data types for beginners: a string which is a series of text, integers which are whole numbers,
floats which are numbers but they contain a decimal portion, and booleans which are either `True` or
`False`. They're binary.
Your assignment in the comment section is to post four variables. Post a string, an integer, a float, and a
boolean. Try and think of a unique example if you can.

And well everybody, those are variables in Python.

---

All right everybody, so we are moving on to type casting. Type casting is the process of converting a
variable from one data type to another. We have various functions to convert a value or variable to a
string, an integer, a float, or a boolean.

Let's create some variables. We will create a name variable. Type in your full name, an age, make up
some age, a GPA for grade point average, let's say 3.2, and a boolean of `is_student`. Are we currently a
student? Let's say that's `True`.

Now, you actually could get the data type of a variable or a value by using the `type` function. Then pass
in a value or variable. However, when I run this, there's no output. So, I need a print statement. We will
print what is returned by the `type` function.

Get the type of our name variable, then print it:

```python

print(type(name))

```

So our `name` variable is a string `str`. Our `age` variable is an integer `int`. `gpa` is a float. `is_student` is
a boolean.

Using these type cast functions we can convert from one data type to another. Here's how.

Let's start with something simple. Let's convert our GPA to an integer. Currently, it's a float. I will
reassign `gpa`:

```python

gpa = int(gpa)
print(gpa)

```

If we type cast 3.2 to a whole integer, what would the result be? A whole integer of three. We truncate
the decimal portion.

Let's convert our age to a floating-point number. We will reassign our variable of age:

```python

age = float(age)

print(age)

```

And it should be a floating point number `25.0`.

Now we'll cover strings. Let's type cast our age to be a string:

```python

age = str(age)

print(type(age))

```

So the result is still going to appear the same `25`. However, it's a string not an integer. And to prove
that I will enclose my age variable with the `type` function. The type of variable `age` is a string.

It would be the same as if we're taking this number and enclosing it within quotes. So this would make a
difference because let's say that I add one to age:

```python

age += 1

```

Well, we would get a type error:


```

TypeError: can only concatenate str (not "int") to str

```

However, if I were to add a string of one to the end, we would be using string concatenation. So let's say
it's my birthday and I add `"1"` to `"25"`:

```python

age += "1"

print(age)

```

The result would be:

```

251

```

I am 251 years old.

So strings and numbers behave differently. With numbers, we can use them within arithmetic
expressions. Strings, not so much.

We will take our name variable and type cast it to a boolean:

```python

name = bool(name)

print(name)

```

This has an interesting result. So I'm going to print `name`. Booleans are either `True` or `False`. If I type
cast my string of text into a boolean that gives me `True`.
Now it really doesn't matter what I write here. If I were to change my name to a single character such as
`"B"`, this would still be `True`.

If our string variable was empty, there were no characters within it, that would actually give us `False`.

We could use this to check to see if somebody enters in their name or not. If somebody types in their
name, then we type cast it to a boolean. If somebody skips entering in their name, that would return
`False`. We could reprompt the user to enter in their name again.

All right, everybody. So, that is type casting. It is the process of converting a variable from one data type
to another. This is especially useful with handling user input because user input is always a string. There
may be at times where we want to convert it to an integer, a float, or a boolean.

And well everybody that is type casting in Python.

---

All right everybody, in this topic I'm going to show you how we can accept user input in Python. We use
the `input` function. It's a function that prompts the user to enter in data and it returns the entered data
as a string.

Here's an example. To accept user input, we will call the `input` function. When I run this program, we
need to enter in data to our console window like so, then hit enter.

However, we need a prompt. We need to tell the user what we want them to type in. So, let's ask a
question. Our prompt will be within quotes. Let's say:

```python

input("What is your name? ")

```

Let's try this again.

```
What is your name?

```

I can type in something. Why don't you go ahead and type in your full name, then hit enter.

Now, with this input, we're not quite doing anything with it. The `input` function is going to return some
data as a string. We can assign it to a variable if we would like.

Let's create a variable of `name`:

```python

name = input("What is your name? ")

```

Then once we have our name, let's print a message. I'll use an F-string:

```python

print(f"Hello, {name}")

```

Let's try this.

```

What is your name? Spongebob

Hello, Spongebob

```

Many people are familiar with Spongebob.

This time we will ask a user how old they are. Let's assign a variable of `age` equals accept some user
input. We need a prompt within quotes:

```python
age = input("How old are you? ")

```

Once we have our age variable, let's print. I'll use an F-string:

```python

print(f"You are {age} years old")

```

What is your name? Type in your name. How old are you? Let's say that I'm 25.

```

Hello, Bro

You are 25 years old

```

All right. So, let's say that it's our birthday. Before we print our age variable, let's say happy birthday.
Since I'm not inserting any variables within this print statement, this doesn't need to be an F-string.
You'll want to use an F-string if you want to insert variables.

Before we display the user's age, let's take the user's age and increase it by one. We could say:

```python

age = age + 1

```

But there's one problem with this. Type in a name. How old are you? And we have a problem. We have a
type error:

```

TypeError: can only concatenate str (not "int") to str

```
When we accept user input, we store that input as a string. Before we increment our age by one, we'll
need to convert it to an integer. We can't normally use strings within arithmetic expressions. But we can
do that with integers and floats, though.

After we accept some user input for our age variable, we could take our age variable and type cast it as
an integer, which we talked about in the previous lesson. So let's say:

```python

age = int(age)

age = age + 1

```

So type in your name, type in an age, and we get this message:

```

Hello, Bro

Happy birthday!

You are 26 years old

```

So strings we can't normally use with arithmetic expressions. We would have to type cast it to an integer
or a float. However, we could condense some of these steps. We're taking up an extra line to type cast
our age as an integer.

What we could do instead is that when we accept our user input, we can enclose the input function
within a type cast to `int`. And that would work the same:

```python

age = int(input("How old are you? "))

```

Type in your name, type in an age, and this works the same. And it takes fewer lines of code and is more
readable.
I would say when we accept user input, it returns that input as a string data type. Then we just have to
type cast it to another data type if we need to. And in this case for age, we do.

---

Now, we'll go over a couple exercises because it's important to practice what you've learned.

In this first exercise, we're going to calculate the area of a rectangle. We need to prompt the user to
enter in a length and the width of a rectangle.

So we will create a variable of `length`. We will accept some user input using the `input` function. What
is our prompt? Let's say:

```python

length = input("Enter the length: ")

```

Let's do this with width. I'll just copy and paste what we have:

```python

width = input("Enter the width: ")

```

So we have the length and the width. To get the area of a rectangle, we have to multiply the length by
the width. So let's say:

```python

area = length * width

```

That is the area. I'm going to print our area because I need to test something:

```python

print(area)
```

Enter the length. Let's say 5 (5 cm). Doesn't matter.

Enter the width. 6.

We get a type error:

```

TypeError: can't multiply sequence by non-int of type 'str'

```

When we accept user input, it returns a value of the string data type. We can't use those strings in
arithmetic expressions. We're multiplying the length times the width. We would need to type cast them
as an integer or a float.

Since we're working with basic geometry such as calculating the area, let's do float. So let's type cast our
user input as a float for both length and...

float. So, let's update our code:

```python

num_one = float(input("Enter the first number: "))

num_two = float(input("Enter the second number: "))

```

Now, let's create an if statement to check which operator the user entered. We'll perform the
corresponding arithmetic operation based on the operator.

```python

if operator == "+":

result = num_one + num_two

elif operator == "-":

result = num_one - num_two

elif operator == "*":


result = num_one * num_two

elif operator == "/":

if num_two != 0:

result = num_one / num_two

else:

result = "Error: Division by zero"

else:

result = "Invalid operator"

```

Finally, let's print the result:

```python

print(f"The result is: {result}")

```

This way, the program will perform addition, subtraction, multiplication, or division based on the user's
input, and handle division by zero and invalid operators gracefully.

A float. So, enclose your input functions with a type cast to `float`. Now we should be able to add those
two numbers together. So let's add 10 and 11, and we get 21.0.

Depending on the operator that the user selects, we'll use some if statements to determine that. We
will check if our `operator` variable is equal to a character of plus. For now, I'll write `pass` as a
placeholder. We'll get back to this later.

Else if our operator is equal to minus, we will use subtraction. For now, I'll write `pass`.

Else if `operator` is equal to an asterisk for multiplication, we will multiply.

Else if our operator is equal to a forward slash for division, we will divide.

If our operator is addition, let's create a variable of `result`:

```python
result = num1 + num2

```

For subtraction, it's going to be:

```python

result = num1 - num2

```

Multiplication would be:

```python

result = num1 * num2

```

Then division would be:

```python

result = num1 / num2

```

Then we just have to print the result:

```python

print(result)

```

Be sure to do this with each of the `elif` statements as well.

Let's see what we have.

Let's add 5.5 + 6.9. That gives us 12.4.


Let's subtract 420 minus 0.69. That gives us 419.31.

Let's test multiplication. Multiply 3.14 times 3.14, which gives us 9.8596.

Then division. Let's divide 69 by 13. And that gives us a really long number.

So you could round a number if you would like. We would enclose our result within the `round` function.
We'll just update each of these print statements. This will round to the nearest whole integer.

So let's divide 420 by 13.

Let's say that we would like three digits after the decimal. Within the `round` function, we could add `,
3` for three decimal places.

Enter operator. Let's use division. Divide 420 by 69. So that gives me 6.087.

So, we can round to a given digit after a decimal. In this case, three places.

What if somebody types in an operator that doesn't exist, like the word pizza? Then I will divide two
numbers.

Well, let's add an else statement. If somebody selects some input that is invalid, let's let them know. I'll
use an f-string:

```python

print(f"{operator} is not a valid operator")

```

Let's try this again.

```

Enter an operator: pizza

Enter the first number: 420


Enter the second number: 69

pizza is not a valid operator

```

Let's say is not a valid operator instead. That makes more sense.

Pizza will be my operator. First number is 420. Second number is 69.

```

pizza is not a valid operator

```

All right everybody, so that is a very simple Python calculator program you can make as a beginner.

---

Hey there, it's me again. In today's topic, we're going to create a weight converter program in Python.
This is an exercise that will follow up the lesson on if statements.

We'll convert pounds to kilogram or kilogram to pounds. The user is going to decide.

We will begin by creating a `weight` variable. We will assign some user input:

```python

weight = float(input("Enter your weight: "))

```

Then we will ask for a unit:

```python

unit = input("Is this weight in Kilogram or Pounds? (K/L): ")

```
We want the user to type in either `K` for kilogram or `L` for pounds. These are capital letters, by the
way.

Using an if statement, let's first check to see if our unit is equal to a capital `K`. That means the current
weight is in kilogram. We need to convert that weight to pounds:

```python

if unit == "K":

weight = weight * 2.205

unit = "lbs"

```

Else if `unit` is equal to `L`, we need to convert to kilogram:

```python

elif unit == "L":

weight = weight / 2.205

unit = "kgs"

```

Else the user did not type in something that was valid:

```python

else:

print(f"{unit} was not valid")

```

At the end of our program, we will print the new weight. I'll use an f-string:

```python

print(f"Your weight is {round(weight, 1)} {unit}")

```
Now, we need a unit of measurement. This is what I'm thinking we'll do within our if and else if
statements. Let's reassign our unit. We're reassigning `unit` to be `lbs` for pounds or `kgs` for kilogram in
our results. This way, we will display our new unit.

Let's take a look.

```

Enter your weight: 180

Is this weight in Kilogram or Pounds? (K/L): L

Your weight is 81.6 kgs

```

Let's try this again.

```

Enter your weight: 81

Is this weight in Kilogram or Pounds? (K/L): K

Your weight is 178.6 lbs

```

Let's make sure that this else statement works, too.

```

Enter your weight: 180

Is this weight in Kilogram or Pounds? (K/L): pizzas

pizzas was not valid

```

So, we're still displaying our output. We would want to avoid that if somebody doesn't type in a valid
unit.

So, let's cut this line and then paste it within the if and elif statements. When we exit the else
statement, we're not printing the output.
So, let's make sure that this works.

```

Enter your weight: 180

Is this weight in Kilogram or Pounds? (K/L): pizzas

pizzas was not valid

```

All right, everybody. Well, that is a weight converter program in Python. I thought this would be a
helpful exercise now that we have finished the section on if statements. And yeah, that is a weight
converter program in Python.

---

Hey everybody. In this topic, we're going to create a temperature conversion program as an exercise.

We'll begin by asking what the current unit of measurement is:

```python

unit = input("Is this temperature in Celsius or Fahrenheit? (C/F): ")

```

Then we will ask for the temperature. I'll store the temperature in a variable named `temp` meaning
temperature:

```python

temp = float(input("Enter the temperature: "))

```

If `unit` is equal to `C`, I'll fill this in momentarily. I'm just going to write `pass` as a placeholder.

Else if `unit` is equal to `F`, we will do something else.


Else, let's print something. Just an error message of some sort using an f-string:

```python

print(f"{unit} is an invalid unit of measurement")

```

Let's test this else statement.

```

Is the temperature in Celsius or Fahrenheit? (C/F): K

Enter the temperature: 100

K is an invalid unit of measurement

```

All right, we know the else statement works.

Let's convert Celsius to Fahrenheit using this formula:

```python

if unit == "C":

temp = round((9 * temp) / 5 + 32, 1)

print(f"The temperature in Fahrenheit is {temp}°F")

```

Let's test this if statement.

```

Is the temperature in Celsius or Fahrenheit? (C/F): C

Enter the temperature: 33

The temperature in Fahrenheit is 91.4°F

```
All right, so this section is working.

Let's work on the else statement.

Else, if our unit is currently in Fahrenheit, we'll convert to Celsius. That formula is:

```python

elif unit == "F":

temp = round((temp - 32) * 5 / 9, 1)

print(f"The temperature in Celsius is {temp}°C")

```

Let's test this.

```

Is the temperature in Celsius or Fahrenheit? (C/F): F

Enter the temperature: 91.4

The temperature in Celsius is 33.0°C

```

Well everybody, that is a simple temperature conversion program in Python.

---

All right people, we're talking about logical operators today. Logical operators allow us to evaluate
multiple conditions. We can link them together. There's three we'll discuss: `or`, `and`, and `not`.

We'll begin with `or`. With `or`, we can check more than one condition. If at least one of those
conditions is true, then the entire statement is true.

Here's an example.
Let's say we have an outdoor event. I will create two variables. One `temp`, meaning temperature. Let's
say that this is in Celsius, 25°C. Pick Fahrenheit if you would like.

And I will create a boolean variable of `is_raining`. I will set that to be `False`. It is currently not raining.

If the temperature is too hot, too cold, or it's raining, then I will cancel this outdoor event.

We'll write an if statement to check that:

```python

if temp > 35 or temp < 0 or is_raining:

print("The outdoor event is cancelled.")

else:

print("The outdoor event is still scheduled.")

```

The temperature is reasonable and `is_raining` is `False`. So we print the else clause:

```

The outdoor event is still scheduled.

```

What if the temperature was really hot, like 36°?

```

The outdoor event is cancelled.

```

What if it's cold? -5°.

```

The outdoor event is cancelled.

```
This condition was true. Therefore, we execute the if statement.

Or what if the temperature is reasonable, but it's raining? `is_raining` is `True`.

```

The outdoor event is cancelled.

```

So with the `or` logical operator, at least one of these conditions needs to be true. If one of these
conditions is true, you could consider the entire statement true.

---

Now let's cover `and`. With `and`, we can link two conditions together. Both conditions must be true in
order for that entire statement to be true.

Again, let's say we have `temp` short for temperature and we have a boolean variable of `is_sunny`. I
will set that to be `True`.

We will check if our `temp` is greater than or equal to 28° **and** `is_sunny` is true.

If this is true, let's print the following:

```python

if temp >= 28 and is_sunny:

print("It is hot outside ")

```

Currently, the temperature is 25°C and it's sunny. This condition was false, but this one is true.

With the `and` logical operator, both conditions must be true in order for us to execute this block of
code.
If our temperature was 30°, well, then both conditions are true:

```

It is hot outside

```

Let's write a few more.

Let's add `elif`:

```python

elif temp <= 0 and is_sunny:

print("It is cold outside and it is sunny.")

```

Let's set the temperature to be 5°. It is cold outside and it is sunny. Both these conditions are true.

You can link as many conditions together as you would like.

Let's see if our temperature is within a certain range:

```python

elif 0 < temp < 28 and is_sunny:

print("It is warm outside and it is sunny.")

```

So, let's say our temperature is 20°C and it's sunny:

```

It is warm outside and it is sunny.

```
---

Now, we have the `not` logical operator. It inverts the condition. We are checking to see if something is
either not false or not true.

Let's check to see if it's not sunny:

```python

elif not is_sunny:

print("It is cloudy ")

```

Basically, `not` does the opposite of what you're looking for. We are checking if `not is_sunny`. If
`is_sunny` is false, then this condition is true.

Let's say our temperature is 28 and `is_sunny` is now false:

```

It is hot outside

It is cloudy

```

What if our temperature was zero?

```

It is cold outside and it is cloudy

```

What if the temperature was reasonable like 20°?

```

It is warm outside and it is cloudy

```
So `not` inverts the condition. If it's true, it's now false. If it's false, it's now true.

All right, everybody. So those are logical operators. They allow us to evaluate multiple conditions.

- With `or`, at least one condition must be true.

- With `and`, both conditions must be true.

- And `not` inverts the condition.

And well everybody, those are logical operators in Python.

---

Hey everybody. So today I got to explain conditional expressions in Python.

A conditional expression is a one-line shortcut for using an if-else statement. If you're familiar with other
programming languages, this is also known as the ternary operator. It behaves similarly.

Using conditional expressions, we can print or assign one of two values based on a condition.

Here's the formula:

```

X if condition else Y

```

Here's a basic example.

We will create a variable for number just `num`. Let's say our number is five.

I'm going to print. Then within our print statement, I will write a conditional expression following this
formula.
Let's check to see if our number is positive:

```python

num = 5

print("positive" if num > 0 else "negative")

```

That will print `positive`.

If our number was `-5`, well, this condition would be false. We would instead print `negative`.

Let's go over another.

Let's check to see if our number is even or odd.

Let's set `num` to be six.

This time I will assign the result to a variable:

```python

num = 6

result = "even" if num % 2 == 0 else "odd"

print(result)

```

Result: `even`.

If it's five, then it's `odd`.

Let's create variables `a` and `b`:

```python
a=6

b=7

```

Let's create a variable of `max_num`:

```python

max_num = a if a > b else b

```

Between `a` and `b`, which is the maximum number? That would be `b` of seven.

Let's find the minimum this time:

```python

min_num = a if a < b else b

```

The minimum number between 6 and 7 is 6.

This time we'll take an age:

```python

age = 25

status = "adult" if age >= 18 else "child"

print(status)

```

Our age is 25. That's greater than or equal to 18, so we will print `adult`.

If our age was 13, then we are a child. We will instead return `child`.
Let's work with the temperature.

```python

temperature = 30

weather = "hot" if temperature > 20 else "cold"

print(f"What's the weather outside today? Based on the temperature it is {weather}.")

```

If our temperature was 20, then the weather is `cold`.

Okay, last example.

We will work with a user role.

I will set this to be `admin`.

We will define a variable of `access_level`:

```python

user_role = "admin"

access_level = "full access" if user_role == "admin" else "limited access"

print(access_level)

```

Our user role is an admin. Let's print our access level and we have `full access`.

But what if we were a guest? Well, then we have `limited access`.

All right, everybody. Those are conditional expressions. They're a one-line shortcut for the if-else
statement. It's similar to the ternary operator in other programming languages.

Using conditional expressions, we can print or assign one of two values based on a condition. You follow
the formula of return X if our condition is true, else return Y if it's false.
And well everybody, those are a few examples of conditional expressions in Python.

---

Hey everybody, in this topic I'm going to cover a few useful string methods that you may be interested
in. Then at the end of this video, we will work on an exercise where we will validate some user input.

As we know, a string is just a series of characters.

Let's ask a user for their full name:

```python

name = input("Enter your full name: ")

```

The first method I'll show you — well technically this is a function — the `len` function will give us the
length of a string. How many characters is it?

We will find the length of our variable `name` after the user types in some input. This function returns
an integer. I'll store that result within a variable:

```python

result = len(name)

print(result)

```

Why don't you go ahead and type in your full name?

The length of this string in my example is eight characters. That does include spaces too. 1 2 3 4 5 6 7 8.

If you ever need the length of a string, there is the `len` function.
Let's move on.

If we were to type our variable `name` followed by a dot, we have access to a whole bunch of different
methods.

We have the `find` method. The `find` method will return the first occurrence of a given character, the
position.

Let's find any spaces:

```python

result = name.find(" ")

print(result)

```

I'll store the result within a variable named `result`.

I will type in my full name. The first occurrence of a space, that's what we set, is at position three.

When working with indexes, we always begin with zero. This first character would have an index of zero,
then 1 2 3. That's why the `find` method returned three instead of four.

Let's find the first occurrence of a capital `B`:

```python

result = name.find("B")

print(result)

```

See, it's zero.

How about `O`? For me, that would be two.


So remember, it's always the first occurrence.

If you need the last occurrence, there is a different method, which is `rfind`. `r` meaning reverse.

We will find the last occurrence of an `O`:

```python

result = name.rfind("O")

print(result)

```

That has a position of five. 0 1 2 3 4 5.

If Python isn't able to locate a given character, it will return `-1`.

Let's find any lowercase `q`s:

```python

result = name.rfind("q")

print(result)

```

Python could not find any lowercase `q`s. The `rfind` method will return `-1` if there are no results.

We can capitalize the first letter in a string by using the `capitalize` function:

```python

name = name.capitalize()

print(name)

```

This method will return a string. I will reassign that to `name`. Then we will print our name capitalized.
I'll be sure to type in my name, all lowercase.

Since this is all one string, only the first letter is capitalized, even though I'm including a first and last
name.

The `upper` method will take all of the characters in a string, then make them all uppercase:

```python

name = name.upper()

print(name)

```

Then I will reassign the result to my `name` variable to overwrite it.

Enter your full name. All of the letters are now uppercase.

There is also `lower` to make all of the characters lowercase:

```python

name = name.lower()

print(name)

```

Yep. All the characters are lowercase.

Now the `isdigit` method will return either `True` or `False` if a string contains only digits. The result is a
boolean, `True` or `False`.

I'll store that within a variable named `result`, then print `result`:

```python

result = name.isdigit()
print(result)

```

So if I were to type in my full name, `isdigit` returns `False`. There are not only digits within that string.

If my string was some combination of alphabetical characters and numbers, this method will still return
`False`. It only returns `True` if my string only contains digits.

I'll just type in `123`. See, that's `True`.

That is the `isdigit` method.

Otherwise, we have `isalpha`.

```python

result = name.isalpha()

print(result)

```

The `isalpha` method will return a boolean `True` or `False` depending if a string contains only
alphabetical characters.

I'll type in my full name.

So, the reason that this came up `False` is because my full name contains a space, which is not an
alphabetical character.

If I typed in my full name excluding any spaces, this would now be `True`.

`isalpha` would also return `False` if my name contained any sort of digits.

`Bro123` is also `False`.


That is the `isalpha` method.

Now let's ask for a phone number:

```python

phone_number = input("Enter your phone number: ")

```

With the phone number, they typically contain dashes.

Let's count how many dashes are going to be in somebody's phone number:

```python

result = phone_number.count("-")

print(result)

```

So, place a character within the `count` method. This method will return an integer.

Let's store that within a variable `result`.

Type in some phone number: `1-234-567-8901`.

We have three dashes within the string. 1 2 3.

That is the `count` method.

We can count how many characters are within a string.

We also have the `replace` method.

Honestly, the `replace` method is probably one of the most useful methods of strings.
We can replace any occurrence of one character with another.

```python

phone_number = phone_number.replace("-", " ")

print(phone_number)

```

This method will return a new string.

I'm going to reassign this to our `phone_number` variable.

Then print the phone number.

Enter your phone number: `1-234-567-8901`.

So, here's my new phone number, but we've replaced all of the dashes with spaces.

Even better yet, we could eliminate all the dashes completely by replacing the dashes or another
character with an empty string:

```python

phone_number = phone_number.replace("-", "")

print(phone_number)

```

This will remove all dashes from the phone number.

```python

# Here's our new phone number without any dashes.

# We've replaced all dashes with an empty string, no characters.

phone_number = "1-234-567-8901"

phone_number = phone_number.replace("-", "")

print(phone_number)
# If you would like a comprehensive list of all of the string methods available to you,

# you can use the help function.

help(str)

# Here's a bunch of methods you might be interested in in the future:

# capitalize, casefold, center, count, encode, endswith, just to name a few.

# Exercise: Validate some user input for a username.

# Rules:

# 1. The username can be no more than 12 characters long.

# 2. The username must not contain any spaces.

# 3. The username must not contain any digits.

username = input("Enter a username: ")

# Check if username is more than 12 characters long

if len(username) > 12:

print("Your username can't be more than 12 characters.")

# Check if username contains any spaces

elif username.find(" ") != -1:

print("Your username can't contain spaces.")

# Check if username contains any digits

elif not username.isalpha():

print("Your username can't contain numbers.")

else:

print(f"Welcome {username}")

# String Indexing explanation and examples

credit_number = "1234-5678-9012-3456"
# Access first character (index 0)

print(credit_number[0]) # Output: 1

# Access second character (index 1)

print(credit_number[1]) # Output: 2

# Access third character (index 2)

print(credit_number[2]) # Output: 3

# Access fourth character (index 3)

print(credit_number[3]) # Output: 4

# Get first four digits (index 0 to 3 inclusive, 4 exclusive)

print(credit_number[0:4]) # Output: 1234

# You can omit the starting index (assumed 0)

print(credit_number[:4]) # Output: 1234

# Get next four digits (index 5 to 8)

print(credit_number[5:9]) # Output: 5678

# Get last 12 digits (index 5 to end)

print(credit_number[5:]) # Output: 5678-9012-3456

# Negative indexing: last character

print(credit_number[-1]) # Output: 6

# Negative indexing: second last character

print(credit_number[-2]) # Output: 5

# Negative indexing: third last character

print(credit_number[-3]) # Output: 4
# Negative indexing: fourth last character

print(credit_number[-4]) # Output: 3

# Step in slicing: every second character

print(credit_number[::2]) # Output: 13-6891346

# Step in slicing: every third character

print(credit_number[::3]) # Output: 146-136

# Practical example: get last four digits of credit card number

last_digits = credit_number[-4:]

print(f"xxxx-xxxx-xxxx-{last_digits}")

# Reverse the string

credit_number = credit_number[::-1]

print(credit_number)

# Format Specifiers examples

price1 = 3.14159

price2 = 987.65

price3 = 12.34

print(f"Price 1 is ${price1:.2f}")

print(f"Price 2 is ${price2:.2f}")

print(f"Price 3 is ${price3:.2f}")

# One decimal place

print(f"Price 1 is ${price1:.1f}")

print(f"Price 2 is ${price2:.1f}")

print(f"Price 3 is ${price3:.1f}")
# Three decimal places

print(f"Price 1 is ${price1:.3f}")

# Allocate 10 spaces

print(f"Price 1 is ${price1:10.2f}")

print(f"Price 2 is ${price2:10.2f}")

print(f"Price 3 is ${price3:10.2f}")

# Zero pad values

print(f"Price 1 is ${price1:010.2f}")

print(f"Price 2 is ${price2:010.2f}")

print(f"Price 3 is ${price3:010.2f}")

# Left justify

print(f"Price 1 is ${price1:<10.2f}")

print(f"Price 2 is ${price2:<10.2f}")

print(f"Price 3 is ${price3:<10.2f}")

# Right justify (default)

print(f"Price 1 is ${price1:>10.2f}")

print(f"Price 2 is ${price2:>10.2f}")

print(f"Price 3 is ${price3:>10.2f}")

# Center align

print(f"Price 1 is ${price1:^10.2f}")

print(f"Price 2 is ${price2:^10.2f}")

print(f"Price 3 is ${price3:^10.2f}")

# Show plus sign for positive numbers

print(f"Price 1 is ${price1:+10.2f}")

print(f"Price 2 is ${price2:+10.2f}")
print(f"Price 3 is ${price3:+10.2f}")

# Space for positive numbers

print(f"Price 1 is ${price1: 10.2f}")

print(f"Price 2 is ${price2: 10.2f}")

print(f"Price 3 is ${price3: 10.2f}")

# Thousand separator

price1 = 3000

price2 = 9870

price3 = 1200

print(f"Price 1 is ${price1:,}")

print(f"Price 2 is ${price2:,}")

print(f"Price 3 is ${price3:,}")

# Mix and match: thousand separator, two decimals, plus sign

price1 = 3009.0

price2 = 9870.0

price3 = 1200.0

print(f"Price 1 is {price1:+,.2f}")

print(f"Price 2 is {price2:+,.2f}")

print(f"Price 3 is {price3:+,.2f}")

# While loops examples

# Example 1: Prompt user for name until they enter something

name = input("Enter your name: ")

while name == "":

print("You did not enter your name.")

name = input("Enter your name: ")


print(f"Hello, {name}")

# Example 2: Prompt user for positive age

age = int(input("Enter your age: "))

while age < 0:

print("Age can't be negative.")

age = int(input("Enter your age: "))

print(f"You are {age} years old.")

# Example 3: Prompt user for food they like until they press Q to quit

food = input("Enter a food you like (Q to quit): ")

while food.upper() != "Q":

print(f"You like {food}")

food = input("Enter another food you like (Q to quit): ")

print("Bye")

# Example 4: Prompt user for number between 1 and 10

num = int(input("Enter a number between 1 and 10: "))

while num < 1 or num > 10:

print(f"{num} is not valid.")

num = int(input("Enter a number between 1 and 10: "))

print(f"Your number is {num}")

# Compound Interest Calculator

# Prompt for principal amount

principal = float(input("Enter the principal amount: "))

while principal <= 0:

print("Principal can't be less than or equal to zero.")

principal = float(input("Enter the principal amount: "))

# Prompt for interest rate


rate = float(input("Enter the interest rate (in %): "))

while rate <= 0:

print("Interest rate can't be less than or equal to zero.")

rate = float(input("Enter the interest rate (in %): "))

# Prompt for time in years

time = int(input("Enter the time in years: "))

while time <= 0:

print("Time can't be less than or equal to zero.")

time = int(input("Enter the time in years: "))

# Calculate compound interest

total = principal * (1 + rate / 100) ** time

# Print the result formatted to 2 decimal places

print(f"Balance after {time} years is ${total:.2f}")

# Alternative version allowing zero values using while True and break

while True:

principal = float(input("Enter the principal amount: "))

if principal < 0:

print("Principal can't be less than zero.")

else:

break

while True:

rate = float(input("Enter the interest rate (in %): "))

if rate < 0:

print("Interest rate can't be less than zero.")

else:

break
while True:

time = int(input("Enter the time in years: "))

if time < 0:

print("Time can't be less than zero.")

else:

break

total = principal * (1 + rate / 100) ** time

print(f"Balance after {time} years is ${total:.2f}")

```

# Python For Loops and Related Concepts

Hey everybody, in this topic I need to explain **for loops**. A for loop will execute a block of code a
fixed number of times. You can iterate over a range, a string, a sequence, anything that is considered
iterable. I'll have more examples for you in future topics.

There is a lot of overlap where you could use either a while loop or a for loop, but for loops tend to be
better in situations where you have to do something only a fixed number of times.

---

## Basic For Loop Example: Counting to 10

```python

for x in range(1, 11):

print(x)

```

- We start at 1 and count to 10.

- The second number in `range` is exclusive, so we use 11 to include 10.

- The code inside the loop is indented.

- You can name the counter anything, but `x` is common.


---

## Counting Backwards

```python

for x in reversed(range(1, 11)):

print(x)

print("Happy New Year")

```

- Use `reversed()` to count backwards.

- The loop counts from 10 down to 1.

- After the loop, print "Happy New Year" outside the loop.

---

## Counting by Steps

Count by twos:

```python

for x in range(1, 11, 2):

print(x)

```

Output: `1 3 5 7 9`

Count by threes:

```python

for x in range(1, 11, 3):


print(x)

```

Output: `1 4 7 10`

---

## Iterating Over a String

```python

credit_card = "1234-5678-9012-3456"

for x in credit_card:

print(x)

```

- `x` holds the current character.

- Iterates over each character including dashes.

---

## Using `continue` and `break`

Counting to 20 but skipping 13:

```python

for x in range(1, 21):

if x == 13:

continue # Skip 13

print(x)

```

Counting to 20 but stopping at 13:


```python

for x in range(1, 21):

if x == 13:

break # Exit loop at 13

print(x)

```

---

# Countdown Timer in Python

```python

import time

my_time = int(input("Enter the time in seconds: "))

for x in range(my_time, 0, -1):

seconds = x % 60

minutes = (x // 60) % 60

hours = x // 3600

print(f"{hours:02}:{minutes:02}:{seconds:02}")

time.sleep(1)

print("Times up!")

```

- Uses `time.sleep(1)` to pause for 1 second.

- Counts down from `my_time` to 1.

- Displays time in `HH:MM:SS` format with zero padding.

- Uses integer division `//` and modulus `%` to calculate hours, minutes, and seconds.
---

# Nested Loops

A nested loop is a loop inside another loop.

Example: Print numbers 1 through 9 three times on separate lines.

```python

for x in range(3): # Outer loop

for y in range(1, 10): # Inner loop

print(y, end="") # Print on same line

print() # New line after inner loop

```

Output:

```

123456789

123456789

123456789

```

---

# Project: Print a Rectangle of Symbols

```python

rows = int(input("Enter the number of rows: "))

columns = int(input("Enter the number of columns: "))

symbol = input("Enter a symbol to use: ")


for _ in range(rows):

for _ in range(columns):

print(symbol, end="")

print()

```

- Outer loop controls rows.

- Inner loop controls columns.

- Prints a rectangle of the chosen symbol.

---

# Collections in Python: Lists, Sets, Tuples

A **collection** is a single variable used to store multiple values.

Example with a list:

```python

fruits = ["apple", "orange", "banana", "coconut"]

print(fruits) # ['apple', 'orange', 'banana', 'coconut']

```

- Use **square brackets** `[]` for lists.

- Use **curly braces** `{}` for sets.

- Use **parentheses** `()` for tuples.

---

## Accessing List Elements by Index

```python
print(fruits[0]) # apple

print(fruits[1]) # orange

print(fruits[2]) # banana

print(fruits[3]) # coconut

```

- Indexing starts at 0.

- Access slices:

```python

print(fruits[0:3]) # ['apple', 'orange', 'banana']

print(fruits[::2]) # ['apple', 'banana']

print(fruits[::-1]) # ['coconut', 'banana', 'orange', 'apple']

```

---

## Iterating Over a List

```python

for fruit in fruits:

print(fruit)

```

- Use singular name for the loop variable for readability.

---

## Useful List Methods

- `append(value)`: Add value to the end.


```python

fruits.append("pineapple")

```

- `remove(value)`: Remove first occurrence of value.

```python

fruits.remove("apple")

```

- `insert(index, value)`: Insert value at index.

```python

fruits.insert(0, "pineapple")

```

- `sort()`: Sort list in ascending order.

```python

fruits.sort()

```

- `reverse()`: Reverse the list.

```python

fruits.reverse()

```

---

## Other Useful Functions and Operators


- `len(fruits)`: Get number of elements.

```python

print(len(fruits)) # e.g., 4

```

- `in` operator: Check if value is in list.

```python

print("apple" in fruits) # True or False

```

- Reassign elements by index:

```python

fruits[0] = "pineapple"

```

---

# Summary

- **For loops** iterate over ranges, strings, or collections.

- Use `continue` to skip an iteration, `break` to exit the loop.

- Use `time.sleep()` to pause execution.

- **Nested loops** are loops inside loops.

- **Collections** store multiple values; lists are ordered and mutable.

- Lists have many useful methods like `append`, `remove`, `sort`, and `reverse`.

---

Feel free to ask if you'd like me to explain any of these topics in more detail!
```python

# Fruits.reverse method example

fruits = ["apple", "orange", "banana", "coconut"]

fruits.reverse()

print(fruits) # Output: ['coconut', 'banana', 'orange', 'apple']

# Note: This reverses the list order, not alphabetical order.

# To reverse alphabetically:

fruits.sort()

fruits.reverse()

print(fruits) # Output: ['orange', 'coconut', 'banana', 'apple']

# Clear the list

fruits.clear()

print(fruits) # Output: []

# Return the index of a value

fruits = ["apple", "orange", "banana", "coconut"]

index_apple = fruits.index("apple")

print(f"Index of apple: {index_apple}") # Output: 0

index_coconut = fruits.index("coconut")

print(f"Index of coconut: {index_coconut}") # Output: 3

# If value not found, raises ValueError

# index_pineapple = fruits.index("pineapple") # Raises ValueError: 'pineapple' is not in list

# Count occurrences of a value

count_banana = fruits.count("banana")

print(f"Bananas found: {count_banana}") # Output: 1

count_pineapple = fruits.count("pineapple")
print(f"Pineapples found: {count_pineapple}") # Output: 0

# Lists: ordered, changeable, duplicates allowed

# Use square brackets []

# Sets: unordered, immutable values but can add/remove elements, no duplicates

fruits_set = {"apple", "orange", "banana", "coconut"}

print(fruits_set) # Order may vary

# Display set methods and attributes

print(dir(fruits_set))

# Length of set

print(len(fruits_set)) # Output: 4

# Check membership

print("pineapple" in fruits_set) # Output: False

# Indexing not supported on sets

# print(fruits_set[0]) # Raises TypeError: 'set' object is not subscriptable

# Add and remove elements

fruits_set.add("pineapple")

print(fruits_set)

fruits_set.remove("apple")

print(fruits_set)

# Pop removes a random element

popped = fruits_set.pop()

print(f"Popped element: {popped}")

print(fruits_set)
# Clear set

fruits_set.clear()

print(fruits_set) # Output: set()

# Sets do not allow duplicates

fruits_set = {"apple", "banana", "coconut"}

fruits_set.add("coconut")

print(fruits_set) # Only one coconut

# Tuples: ordered, unchangeable, duplicates allowed, faster than lists

fruits_tuple = ("apple", "orange", "banana", "coconut", "coconut")

print(fruits_tuple)

# Tuple methods: count and index

print(dir(fruits_tuple))

# Length of tuple

print(len(fruits_tuple)) # Output: 5

# Membership check

print("pineapple" in fruits_tuple) # Output: False

# Index of element

index_apple = fruits_tuple.index("apple")

print(f"Index of apple: {index_apple}") # Output: 0

# Count occurrences

count_coconut = fruits_tuple.count("coconut")

print(f"Coconuts found: {count_coconut}") # Output: 2

# Iterate over tuple


for fruit in fruits_tuple:

print(fruit)

# Summary:

# Lists: ordered, changeable, duplicates allowed

# Sets: unordered, immutable values but can add/remove elements, no duplicates

# Tuples: ordered, unchangeable, duplicates allowed, faster than lists

# Shopping Cart Program Example

foods = []

prices = []

total = 0.0

while True:

food = input("Enter a food to buy (Q to quit): ")

if food.lower() == "q":

break

else:

foods.append(food)

price = float(input(f"Enter the price of {food}: "))

prices.append(price)

print("\n----- Your Cart -----")

for food in foods:

print(food, end=" ")

print()

for price in prices:

total += price

print(f"\nYour total is ${total:.2f}")


# 2D Lists Example

fruits = ["apple", "orange", "banana", "coconut"]

vegetables = ["celery", "carrots", "potatoes"]

meats = ["chicken", "fish", "turkey"]

groceries = [fruits, vegetables, meats]

print(groceries) # Prints 2D list

# Accessing elements

print(groceries[0]) # Fruits list

print(groceries[0][0]) # 'apple'

print(groceries[1][2]) # 'potatoes'

# Nested loops to iterate 2D list

for collection in groceries:

for item in collection:

print(item, end=" ")

print()

# 2D Tuple Example: numpad

numpad = (

(1, 2, 3),

(4, 5, 6),

(7, 8, 9),

("*", 0, "#")

for row in numpad:

for num in row:

print(num, end=" ")

print()
# Quiz Game Example

questions = (

"How many elements are in the periodic table?",

"Which animal lays the largest eggs?",

"What is the most abundant gas in Earth's atmosphere?",

"How many bones are in the human body?",

"Which planet in the solar system is the hottest?"

options = (

("A. 100", "B. 110", "C. 118", "D. 120"),

("A. Ostrich", "B. Whale", "C. Eagle", "D. Crocodile"),

("A. Nitrogen", "B. Oxygen", "C. Carbon Dioxide", "D. Hydrogen"),

("A. 206", "B. 210", "C. 201", "D. 208"),

("A. Venus", "B. Venus", "C. Mars", "D. Mercury")

answers = ("C", "D", "A", "A", "B")

guesses = []

score = 0

question_num = 0

for question in questions:

print("\n--------------------")

print(question)

for option in options[question_num]:

print(option)

guess = input("Enter A, B, C, or D: ").upper()

guesses.append(guess)

if guess == answers[question_num]:

score += 1
print("Correct!")

else:

print(f"Incorrect. The correct answer is {answers[question_num]}.")

question_num += 1

print("\n----- Results -----")

print(f"Score: {score} out of {len(questions)}")

print("Your guesses:", guesses)

print("Correct answers:", answers)

```

```python

# Print answers with space separation

for answer in answers:

print(answer, end=" ")

print() # New line

# Print guesses with space separation

for guess in guesses:

print(guess, end=" ")

print() # New line

# Calculate score as a percentage

score = int(score / len(questions) * 100)

print(f"Your score is {score}%")

# Example runs:

# User inputs: A B C D A

# Output:

#CDAB

#ABCDA

# Your score is 0%
# User inputs all correct:

# Output:

#CDAB

#CDAB

# Your score is 100%

# User inputs some incorrect:

# Output:

#CDAB

#CCCCC

# Your score is 20%

# --- Dictionaries explanation and examples ---

# Create a dictionary of countries and capitals

capitals = {

"USA": "Washington DC",

"India": "New Delhi",

"China": "Beijing",

"Russia": "Moscow"

# Get capital of USA

print(capitals.get("USA")) # Washington DC

# Get capital of India

print(capitals.get("India")) # New Delhi

# Get capital of Japan (not in dictionary)

print(capitals.get("Japan")) # None

# Check if key exists


if capitals.get("Japan"):

print("Capital exists")

else:

print("Capital doesn't exist") # This will print

if capitals.get("Russia"):

print("Capital exists") # This will print

else:

print("Capital doesn't exist")

# Update dictionary: add Germany

capitals.update({"Germany": "Berlin"})

print(capitals)

# Update existing key USA capital

capitals.update({"USA": "Detroit"})

print(capitals)

# Remove China

capitals.pop("China")

print(capitals)

# Remove last inserted item

capitals.popitem()

print(capitals)

# Clear dictionary

capitals.clear()

print(capitals) # {}

# Get all keys

keys = capitals.keys()
print(keys) # dict_keys([...])

# Iterate over keys

for key in capitals.keys():

print(key)

# Get all values

values = capitals.values()

print(values) # dict_values([...])

# Iterate over values

for value in capitals.values():

print(value)

# Get all items (key-value pairs)

items = capitals.items()

print(items) # dict_items([...])

# Iterate over key-value pairs

for key, value in capitals.items():

print(f"{key}: {value}")

# --- Concession stand program using dictionary ---

menu = {

"pizza": 5.00,

"nachos": 3.50,

"popcorn": 4.00,

"fries": 2.50,

"chips": 1.50,

"soft pretzels": 3.00,

"soda": 2.00,
"lemonade": 2.00

cart = []

total = 0.0

print("Menu:")

print("--------------------")

for item, price in menu.items():

print(f"{item:15} : ${price:.2f}")

print("--------------------")

while True:

food = input("Select an item (Q to quit): ").lower()

if food == "q":

break

elif menu.get(food) is not None:

cart.append(food)

else:

print(f"Sorry, {food} is not on the menu.")

print("\nYour cart:")

for food in cart:

print(food, end=" ")

print()

for food in cart:

total += menu.get(food)

print(f"\nTotal is ${total:.2f}")

# --- Random module examples ---


import random

# Random integer between 1 and 6 (dice roll)

dice_roll = random.randint(1, 6)

print(f"Dice roll: {dice_roll}")

# Random integer between 1 and 20 (d20 roll)

d20_roll = random.randint(1, 20)

print(f"D20 roll: {d20_roll}")

# Using variables for range

low = 1

high = 100

rand_num = random.randint(low, high)

print(f"Random number between {low} and {high}: {rand_num}")

# Random float between 0 and 1

rand_float = random.random()

print(f"Random float: {rand_float}")

# Random choice from sequence

options = ("rock", "paper", "scissors")

choice = random.choice(options)

print(f"Random choice: {choice}")

# Shuffle a list

cards = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]

random.shuffle(cards)

print(f"Shuffled cards: {cards}")

# --- Number guessing game ---


import random

lowest_num = 1

highest_num = 100

answer = random.randint(lowest_num, highest_num)

print(f"(Debug) The answer is: {answer}")

guesses = 0

is_running = True

print("Python Number Guessing Game")

print(f"Select a number between {lowest_num} and {highest_num}")

while is_running:

guess = input("Enter your guess: ")

if guess.isdigit():

guess = int(guess)

guesses += 1

if guess < lowest_num or guess > highest_num:

print("That number is out of range.")

continue

if guess == answer:

print(f"Congratulations! You guessed the number in {guesses} guesses.")

is_running = False

elif guess < answer:

print("Too low. Try again.")

else:

print("Too high. Try again.")

else:

print(f"Invalid guess. Please select a number between {lowest_num} and {highest_num}.")

```
```python

# Let's add the remaining multiply and divide functions

def multiply(x, y):

z=x*y

return z

def divide(x, y):

if y == 0:

return "Error: Division by zero"

z=x/y

return z

# Example usage:

z_add = add(1, 2)

print(f"Add: {z_add}") # Output: 3

z_subtract = subtract(5, 3)

print(f"Subtract: {z_subtract}") # Output: 2

z_multiply = multiply(4, 6)

print(f"Multiply: {z_multiply}") # Output: 24

z_divide = divide(10, 2)

print(f"Divide: {z_divide}") # Output: 5.0

z_divide_zero = divide(10, 0)

print(f"Divide by zero: {z_divide_zero}") # Output: Error: Division by zero

```

```python

# Then divide. X / Y return Z.

# Let's invoke our add function. Pass in two numbers, one and two.
z = add(1, 2)

print(z) # After adding these two numbers together, the result is three.

# What about subtract? Subtract 1 and two.

z = subtract(1, 2)

print(z) # The result is -1.

# Multiply.

z = multiply(1, 2)

print(z) # The result is two.

# Then divide.

z = divide(1, 2)

print(z) # 1 / 2 is 0.5.

# After we resolve this function, a value is returned.

# Just imagine that after we finish this function, this function becomes whatever is returned:

# three, -1, two, 0.5 respectively.

# Let's write something a little more complex.

# We will create a function to create a full name.

def create_name(first, last):

# Capitalize the user's first name.

first = first.capitalize()

# Capitalize the user's last name.

last = last.capitalize()

# Return the user's first name plus their last name with a space in between.

return first + " " + last

# Outside of the function, let's create a full name variable.

full_name = create_name("bro", "code")


print(full_name) # Output: Bro Code

# Let's try this with a different name.

full_name = create_name("spongebob", "squarepants")

print(full_name) # Output: Spongebob Squarepants

# Using the return statement, you can return some data back to the place in which you call a function.

# Well, everybody, that's a function.

# It's a section of reusable code.

# To call a function, you type the function's name, add a set of parentheses.

# You can send a function some data, which are known as arguments,

# but you'll need a matching set of parameters.

# You also do have the option of returning some data back to the place in which you invoke a function.

# We'll be using functions a lot in the future, but we will get more practice with them.

# And those are functions in Python.

# ------------------------------------------------------------

# Default Arguments

def net_price(list_price, discount=0, tax=0.05):

# Calculate net price using the formula:

# list_price * (1 - discount) * (1 + tax)

return list_price * (1 - discount) * (1 + tax)

# Example usage:

print(f"My total is ${net_price(500):.2f}") # No discount, 5% tax, output: 525.00

# Passing discount argument:

print(f"My total is ${net_price(500, 0.1):.2f}") # 10% discount, 5% tax, output: 472.50


# Passing discount and tax arguments:

print(f"My total is ${net_price(500, 0.1, 0):.2f}") # 10% discount, no tax, output: 450.00

# Default arguments make functions more flexible and reduce the number of arguments you have to
pass in.

# ------------------------------------------------------------

# Exercise: Count Up Timer

import time

def count(start=0, end=10):

for x in range(start, end + 1):

print(x)

time.sleep(1)

print("Done")

# Invoke with default start:

count(end=10)

# Invoke with custom start and end:

count(15, 30)

# ------------------------------------------------------------

# Keyword Arguments

def hello(greeting, title, first, last):

print(f"{greeting} {title} {first} {last}")

# Positional arguments:

hello("Hello", "Mr.", "Spongebob", "Squarepants")


# Keyword arguments (order doesn't matter):

hello(title="Mr.", greeting="Hello", last="Squarepants", first="Spongebob")

# Mixing positional and keyword arguments (positional first):

hello("Hello", title="Mr.", first="Spongebob", last="Squarepants")

# ------------------------------------------------------------

# Keyword arguments in built-in print function

for x in range(1, 11):

print(x, end=" ") # end is a keyword argument, prints numbers separated by space

print() # New line

print("1", "2", "3", "4", "5", sep="-") # sep is a keyword argument, separates with dash

# ------------------------------------------------------------

# Exercise: Generate Phone Number with Keyword Arguments

def get_phone(country_code, area_code, first, last):

return f"{country_code}-{area_code}-{first}-{last}"

phone_num = get_phone(country_code=1, area_code=123, first=456, last=7890)

print(phone_num) # Output: 1-123-456-7890

# ------------------------------------------------------------

# Arbitrary Arguments
# Example: Add function with fixed parameters

def add(a, b):

return a + b

print(add(1, 2)) # Output: 3

# Modify to accept arbitrary number of arguments using *args

def add(*args):

total = 0

for num in args:

total += num

return total

print(add(1, 2, 3)) # Output: 6

print(add(4, 5, 6, 7)) # Output: 22

print(add(10)) # Output: 10

# Example: Display name with arbitrary arguments

def display_name(*args):

for arg in args:

print(arg, end=" ")

print()

display_name("Spongebob", "Squarepants")

display_name("Spongebob", "Herald", "Squarepants")

display_name("Dr.", "Spongebob", "Herald", "Squarepants")

# ------------------------------------------------------------

# Arbitrary Keyword Arguments (**kwargs)

def print_address(**kwargs):
# Print type to show it's a dictionary

print(type(kwargs)) # <class 'dict'>

# Print all values

for value in kwargs.values():

print(value)

# Print all keys

for key in kwargs.keys():

print(key)

# Print all key-value pairs

for key, value in kwargs.items():

print(f"{key}: {value}")

print_address(

street="123 Fake Street",

city="Detroit",

state="Michigan",

zip="54321",

apartment=100

# ------------------------------------------------------------

# Exercise: Shipping Label with *args and **kwargs

def shipping_label(*args, **kwargs):

# Print positional arguments (name parts)

for arg in args:

print(arg, end=" ")

print()
# Print street and apartment if present

if 'apartment' in kwargs:

print(f"{kwargs.get('street')}, Apt {kwargs.get('apartment')}")

else:

print(kwargs.get('street'))

# Print city, state, zip

print(f"{kwargs.get('city')}, {kwargs.get('state')} {kwargs.get('zip')}")

# Correct order: *args first, then **kwargs

shipping_label(

"Dr.", "Spongebob", "Squarepants", "III",

street="123 Fake Street",

apartment=100,

city="Detroit",

state="Michigan",

zip="54321"

# Note: Reversing *args and **kwargs in parameters will cause a syntax error.

```

```python

def shipping_label(*args, **kwargs):

# Print positional arguments (name parts)

for arg in args:

print(arg, end=" ")

print()

# Check if 'apartment' is in kwargs

if 'apartment' in kwargs:

print(f"{kwargs.get('street')}, Apt {kwargs.get('apartment')}")


# Check if 'PO box' is in kwargs

elif 'PO box' in kwargs:

print(f"{kwargs.get('street')}, PO Box {kwargs.get('PO box')}")

else:

print(kwargs.get('street'))

# Print city, state, and zip

print(f"{kwargs.get('city')}, {kwargs.get('state')} {kwargs.get('zip')}")

# Example usage:

shipping_label(

"Dr.", "Spongebob", "Squarepants", "III",

street="123 Fake Street",

PO_box=10001,

city="Detroit",

state="Michigan",

zip="54321"

```

---

# Iterables in Python

An **iterable** is any object or collection that can return its elements one at a time, allowing it to be
looped over.

Example with a list:

```python

numbers = [1, 2, 3, 4, 5]

for number in numbers:


print(number)

```

- Use descriptive variable names for clarity.

- You can iterate backwards using `reversed()`:

```python

for number in reversed(numbers):

print(number, end=" ")

```

- You can customize the end character in `print`:

```python

for number in numbers:

print(number, end="-")

```

---

# Iterables: Tuples, Sets, Strings, Dictionaries

- Tuples are iterable:

```python

numbers = (1, 2, 3, 4, 5)

for number in numbers:

print(number)

```

- Sets are iterable but **not reversible**:


```python

fruits = {"apple", "orange", "banana", "coconut"}

for fruit in fruits:

print(fruit)

# reversed(fruits) will raise TypeError

```

- Strings are iterable:

```python

name = "Bro Code"

for char in name:

print(char, end=" ")

```

- Dictionaries iterate over keys by default:

```python

my_dict = {"A": 1, "B": 2, "C": 3}

for key in my_dict:

print(key) # prints keys

for value in my_dict.values():

print(value) # prints values

for key, value in my_dict.items():

print(f"{key} = {value}")

```

---

# Membership Operators: `in` and `not in`


Test if a value exists in a sequence (string, list, tuple, set, dictionary keys):

```python

word = "apple"

letter = input("Guess a letter: ")

if letter in word:

print(f"There is a {letter}")

else:

print(f"{letter} was not found")

```

- `not in` returns `True` if value is **not** found:

```python

if letter not in word:

print(f"{letter} was not found")

```

Example with a set:

```python

students = {"Spongebob", "Patrick", "Sandy"}

student = input("Enter student name: ")

if student in students:

print(f"{student} is a student")

else:

print(f"{student} was not found")

```
Example with dictionary keys:

```python

grades = {"Sandy": "A", "Squidward": "B", "Spongebob": "C", "Patrick": "D"}

student = input("Enter student name: ")

if student in grades:

print(f"{student}'s grade is {grades[student]}")

else:

print(f"{student} was not found")

```

Example checking multiple conditions:

```python

email = "[email protected]"

if "@" in email and "." in email:

print("Valid email")

else:

print("Invalid email")

```

---

# List Comprehensions in Python

A concise way to create lists:

```python

# Traditional loop

doubles = []

for x in range(1, 11):


doubles.append(x * 2)

print(doubles)

```

Equivalent list comprehension:

```python

doubles = [x * 2 for x in range(1, 11)]

print(doubles)

```

Examples:

```python

# Triples

triples = [y * 3 for y in range(1, 11)]

# Squares

squares = [z * z for z in range(1, 11)]

# Uppercase fruits

fruits = ["apple", "orange", "banana", "coconut"]

fruits = [fruit.upper() for fruit in fruits]

# First letters of fruits

fruit_chars = [fruit[0] for fruit in fruits]

# Filter positive numbers

numbers = [1, -2, 3, -4, 5, -6, 8, -24, -6, 8, -7]

positive_nums = [num for num in numbers if num >= 0]

# Filter negative numbers


negative_nums = [num for num in numbers if num < 0]

# Even numbers

even_nums = [num for num in numbers if num % 2 == 0]

# Odd numbers

odd_nums = [num for num in numbers if num % 2 == 1]

# Passing grades

grades = [85, 42, 79, 90, 56, 61, 30]

passing_grades = [grade for grade in grades if grade >= 60]

```

---

# Match Case Statements in Python (Python 3.10+)

Alternative to multiple `if-elif-else`:

```python

def day_of_week(day):

match day:

case 1:

return "Sunday"

case 2:

return "Monday"

case 3:

return "Tuesday"

case 4:

return "Wednesday"

case 5:

return "Thursday"
case 6:

return "Friday"

case 7:

return "Saturday"

case _:

return "Not a valid day"

print(day_of_week(1)) # Sunday

print(day_of_week(7)) # Saturday

print(day_of_week("pizza")) # Not a valid day

```

Example checking if weekend:

```python

def is_weekend(day):

match day:

case "Sunday" | "Saturday":

return True

case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":

return False

case _:

return None # or raise error

print(is_weekend("Sunday")) # True

print(is_weekend("Monday")) # False

```

---

# Summary
- Use `*args` and `**kwargs` for arbitrary arguments in functions.

- Iterables include lists, tuples, sets, strings, and dictionaries.

- Membership operators `in` and `not in` test for presence in sequences.

- List comprehensions provide concise list creation with optional filtering.

- Match case statements simplify multiple conditional branches.

Feel free to ask for examples or explanations on any of these topics!

```

In this video, we will call the function `is_weekend` and pass in a day of the week such as Monday. So, is
Sunday the weekend? That is true. Monday? That is false. Saturday? That is true. And we do have a
wildcard case. If there are no matches, is pizza a day of the weekend? That is false.

There is a way we can modify this match case, too. We tend to be repeating ourselves a lot. The days
Monday through Friday all return false. We're going to use the `or` logical operator, which is
represented with a vertical bar. If the case is Saturday or Sunday, return true. If the case is Monday or
Tuesday, or Wednesday, or Thursday, or Friday, then we will return false. We can keep our wildcard
case.

So, is Saturday part of the weekend? That is true. Is Monday? False. Sunday? True. Friday? False. And
pizza? We have our wildcard case that gives us false.

All right everybody, so those are match case statements. They're similar to switches in other
programming languages. They're an alternative to using many else-if statements. We execute some
code if a value matches a case. The benefit is that the code is cleaner and the syntax is more readable.
And well everybody, those are match case statements in Python.

---

Hello friends, it's me again. Today I'm going to explain modules. A module is just a Python file containing
code you want to include in your program. You use the `import` keyword to include a module. You can
use built-in modules or create your own. Sometimes it's useful to break up a large program into reusable
separate files.

For a list of all the modules found within the standard Python library, you can use the `help` function,
pass in the word `"modules"`, and then we would need to print this. Here are many of the different
modules available to you. A few you may recognize would be `math`, `string`, `time`. One of my favorite
names of a module is the `pickle` module. Unfortunately, it doesn't have anything to do with pickles. It's
used for serialization.
To list all of the different variables and functions found within a module, you can place that name of the
module within the `help` function. For example, with the `math` module, here are a few different
variables we would have access to and a few different functions.

To include a module, we would type:

```python

import math

```

I now have access to everything found within the `math` module, including those variables and
functions. To access those variables and functions, I would normally type the name of the module dot
the name of the variable or function such as `pi`. Then let's print this:

```python

print(math.pi)

```

Pi from the math module is 3.14 and some change.

Another way to import is to type:

```python

import math as M

```

You can give your module a nickname, an alias, whatever you think of such as `M`. `M` short for math.
We would no longer refer to this module as `math`. We would refer to it as our alias `M`. Using an alias
would reduce some of the typing you have to use if you have a very long module name.

Another way to import is to use:

```python
from math import pi

```

You would no longer need the module name. `pi` would be included within our namespace. However, I
tend to not use `from import` as much just because it's possible there could be name conflicts.

Here's an example. Let's say:

```python

from math import e

print(e)

```

`e` is an exponential constant. `e` is 2.71.

What if I was to create a program where we have four variables named `a`, `b`, `c`, `d`:

```python

a=1

b=2

c=3

d=4

```

Then I'm going to print `e` from the math module to the power of `a`:

```python

print(e ** a)

print(e ** b)

print(e ** c)

print(e ** d)

```
Here are the results.

Let's say we have a different variable `e`:

```python

e=5

print(e ** e)

```

We have imported `e` from the math module. When we have declared all of these variables, technically
what we've done is we have created another version of `e`. We will end up using the second version
rather than the version that we have imported from the math module. All my results are now different
and it's possible you may not realize it.

I like to be more explicit. I'm going to import `math`. If I'm using a variable or function from a module, I
much prefer to prefix that variable name or function with the name of the module in which it's from:

```python

import math

print(math.e ** a)

print(math.e ** b)

print(math.e ** c)

print(math.e ** d)

print(math.e ** e)

```

And these results are to be expected.

Now, to create a module, what we're going to do is right click on our project folder, go to new Python
file, think of a module name, maybe `example`, then click Python file. We now have two tabs: `main`
and `example`.

Declare whatever you would like within this module. Let's create our own variable `pi`:
```python

pi = 3.14159

```

Then a few functions. Let's create a function to square an argument that's passed in:

```python

def square(x):

return x ** 2

```

Let's define a cube function:

```python

def cube(x):

return x ** 3

```

Maybe a circumference function:

```python

def circumference(radius):

return 2 * pi * radius

```

Then an area function to calculate the area of a circle:

```python

def area(radius):

return pi * radius ** 2

```
All right, here is our example module. Within our main Python program, let's import the name of our
module which we named `example`:

```python

import example

```

We now have access to everything within this module. I'm going to declare a variable `result` and set it
to the name of my module's `pi`:

```python

result = example.pi

print(result)

```

Let's utilize the square function:

```python

result = example.square(3)

print(result) # 9

```

Let's use the cube function:

```python

result = example.cube(3)

print(result) # 27

```

Circumference:

```python
result = example.circumference(3)

print(result) # 18.84954

```

Then area:

```python

result = example.area(3)

print(result) # 28.27431

```

That's how to create your own module. It can be useful at times to separate your program into
individual files.

All right, everybody. In conclusion, a module is just a file containing code you want to include in your
program. You use `import` to include a module. You can use built-in modules or create your own. If you
do need a list of the modules available to you, again, you can use the `help` function, then pass in the
word `"modules"`. And well everybody, that's how to get started with modules in Python.

---

Hey friends, it's me again. Today I'm going to explain both variable scope and scope resolution. Variable
scope is where a variable is both visible and accessible. With scope resolution, when we're using a
variable, there is a certain order known as the LEGB rule in which we locate that variable: Local,
Enclosed, Global, Built-in. We'll get to this momentarily.

Let's begin with variable scope. I have two functions, `function_one` and `function_two`. Within
`function_one`, `a = 1`, then we print `a`. Within `function_two`, `b = 2`, then we print `b`.

If I were to invoke these functions:

```python

function_one()

function_two()

```
We would print 1 then 2.

Variables declared within a function have a local scope. Variable `a` is local to `function_one`. Variable
`b` is local to `function_two`.

Within `function_one`, if I were to print `b`, and within `function_two`, if I were to print `a`, we would
run into a name error:

```

NameError: name 'b' is not defined

```

And the same thing would apply with `a`. Functions can't see inside of other functions.

Imagine that we're `function_one`. This is our house. We can see everything that's going on inside of our
house, but `function_two` is our neighbor's house. We can't see what's going on inside of our neighbor's
house. We have no idea what `b` is with `function_two`. `function_two` has no idea what `a` is.

That's where variable scope comes in. It's where a variable is visible and accessible. Functions can't see
inside of other functions, but they can see inside of their own function. That's why we sometimes pass
arguments to functions so that our functions are aware of them.

Using this concept, we could create different versions of the same variable. Let's rename `a` to be `x`
and `b` to be `x` as well. Then I will print `x`. We have two different versions of `x`: a local version of `x`
found within `function_one` and a local version of `x` found within `function_two`.

Whenever we utilize a variable, we will first look to see if there's any local instance of that variable. If
there isn't, we would move to the enclosed scope.

With an enclosed scope, one example is when you have a function declared within another function. I'm
going to place `function_two` within `function_one`. This is allowed in Python. This is a more advanced
concept. We'll cover this more in the future.

So, I'm going to eliminate this print statement. Let's get rid of `function_two`. At the end of
`function_one`, we will invoke `function_two`. Like I said, it's pretty complex. We won't be using this
until much later.
Within `function_two`, if I was to print `x`, we would use the local version where `x = 2`. If I was to
eliminate this variable declaration, we would use the enclosed version instead where `x = 1`.

There's an order of operations: use any local variables first, then enclosed variables. We're printing `x`
within `function_two`. Since `x` wasn't found within the local scope, we would use `x` within the
enclosed scope. But like I said, that's a more advanced topic. You should at least be aware of it.

Let's move on to the global scope. Global meaning outside of any functions. I will eliminate these
variable declarations. Within `function_one`, we're printing `x` and within `function_two`, we're also
printing `x`. I will declare a global version of `x` where `x = 3`. `x` is outside of any functions.

When I run this program, we're printing 3 twice. Once for `function_one` and once for `function_two`.
There's no longer a local version of `x` for both of these functions. If there were, we would end up using
these local versions instead. `function_one` prints 1, `function_two` prints 2.

If there's no local version as well as no enclosed version, we would move on to the global version where
`x = 3`.

Last in our order is built-in. I think what we'll do though is:

```python

from math import e

print(e)

```

`e` is an exponential constant. I'm going to print what `e` is. `e` is 2.71. `e` is built-in.

I will create a function to print `e`:

```python

def function_one():

print(e)

function_one()
```

If I was to set `e` to be a different value like 3, what we're doing technically is creating two different
versions of `e`. Variables can share the same name as long as they're within a different scope. We have a
built-in version of `e` and a global version of `e`.

If I was to print `e` now, it would print my global version because using the LEGB order, we would first
look for any local version of `e`, then enclosed version, then global, which we do have one of, then lastly
built-in.

All right, everybody. So in conclusion, variable scope is just where a variable is both visible and
accessible. Python has a scope resolution order LEGB. If we're using a variable, we will first look in the
local scope for that variable. If we don't find that variable in the local scope, we will move over to an
enclosed scope, then global, then built-in. We will have more practice with this in the future. And well
everybody, that is both variable scope and scope resolution in Python.

---

Hey everybody. So today I got to talk about this if statement:

```python

if __name__ == "__main__":

```

When you see this if statement, it's usually followed by a call to a function named `main` or something
similar. A majority of the driving code behind a program is usually found within some sort of main
method.

When you see this if statement, basically speaking, it means that this script can be imported or it can run
standalone. Functions and classes in this module can be reused in other programs without the main
block of code running.

Sometimes you would like the functionality of a program without executing the main body of code. A
good example could be a library. In a Python library, we would like to import some of the useful
functions such as the math module. But if we were to run that library directly instead of importing it, we
could instead display a help page. But if we're importing that library, we don't necessarily want to
display that help page, only if we're running it directly.
In many Python scripts, you'll see the statement:

```python

if __name__ == "__main__":

```

If we're not running this program directly, don't do it.

In this example, we're going to delete our main Python script. Be sure to recreate it at the end of this
topic in case I forget to mention that.

We will create two new scripts: go to File > New > Python File, `script_one`, and File > New > Python File,
`script_two`.

We have to add new run configurations for `script_one` and `script_two`. So if you go to the top, go to
Run > Edit Configurations. We will add a new run configuration. Select Python. Select a new script path
to `script_one`. Okay. Apply.

Again, we have to do this with `script_two`. Add Python. Select a script path of `script_two`. Okay.
Apply. Then okay.

Using this drop-down menu, we can select which run configuration we would like. Would we like to run
our main Python file, but we have deleted it? Do we want to run `script_one` or `script_two`?

For the time being, we'll select `script_one`.

Within `script_one`, if I was to print, then call the `__dir__` function (meaning directory). Python has all
of these built-in attributes. If you're not familiar with object-oriented programming, for now, think of an
attribute as a variable. `__name__` is a special type of variable. `__dunder__` meaning double
underscore.

If I was to print what's contained within `__name__`, we would receive a string of `"__main__"`. That's
why in a script you may see the statement:

```python

if __name__ == "__main__":
```

If so, then you usually call a function named `main` to start your program.

I'm going to undo that.

So let's import `script_two`:

```python

from script_two import *

```

Within `script_two`, I will print `__name__`. And we'll see what's within it.

Again, I'm running `script_one`.

Within `script_two`, `__name__` is equal to the string `"script_two"`, the name of the Python script.

However, within `script_one`, `__name__` is equal to the string `"__main__"`. This means I am running
`script_one` directly.

Let's delete this import.

Then go to `script_two`. Import `script_one`:

```python

import script_one

```

We're now going to change our run configuration from `script_one` to `script_two`. We are running
`script_two` directly.

Now `__name__` within `script_one` is the name of the Python script `script_one`.
`__name__` within `script_two` is now `"__main__"`.

So by adding this if statement of:

```python

if __name__ == "__main__":

```

we can check to see which file is being run directly.

If `__name__ == "__main__"`, we will call a function `main` to contain the main body of our program.

But we need to define this function:

```python

def main():

# Our main function will contain the majority of our Python code.

# Anything that's not already within a function.

print("This is script one")

def favorite_food(food):

print(f"Your favorite food is {food}")

favorite_food("pizza")

print("Goodbye")

```

We're going to run `script_one`. Run it.

Here's the result. From the top down, all of our code is within functions. We skip over it because we're
not calling it quite yet. The first thing we do in this program is check this if statement. If `__name__` is
equal to `"__main__"`. Are we running this program directly? Which we are. We're running `script_one`.
If so, call the `main` method to start the program.

We print:

```

This is script one

Your favorite food is pizza

Goodbye

```

Now I'm going to go to `script_two`. Delete our print statement. Change the run configuration to
`script_two` and run it. Nothing should happen. That's good.

Now, if we were missing this if statement of `if __name__ == "__main__"`, then we delete our main
function. Here's what would happen. We're importing `script_one`, but we're running `script_two`. This
is script one. Your favorite food is pizza. Goodbye. I don't want this code to execute. We're not running
it directly. That's why we have that if statement.

If under name is equal to main, I only want to run this code if we're running it directly.

So what we'll do within `script_two` now is define a function of `favorite_drink`. There's one parameter
of `drink`. I will print the following message:

```python

def favorite_drink(drink):

print(f"Your favorite drink is {drink}")

```

Let's print the message:

```python

print("This is script two")

```
We will call from `script_one` the `favorite_food` function. Pass in your favorite food. This time I'll say
sushi.

Let's call our favorite drink function:

```python

favorite_drink("coffee")

```

Then we will print goodbye.

Okay, we are running `script_two`:

```

This is script two

Your favorite food is sushi

Your favorite drink is coffee

Goodbye

```

We're running `script_two`, but we're importing the functionality of the `favorite_food` function from
`script_one`.

Sometimes from another Python script, you want to borrow something, but you don't want to run the
main body of code directly. I just want to borrow this function from `script_one`, and that's it.

`script_two` can be run as a standalone program, but I can't import it without this body of code running.
I can add that if statement:

```python

if __name__ == "__main__":

main()

```
So let's call a function `main`, then place our main body of code within it.

If I run `script_two`, we have the same message.

So by adding this if statement of `if __name__ == "__main__"`, this script can be run as a standalone
program or it can be imported.

A more practical example of this could be a Python library. You can import the library for functionality,
but if you run the library directly, you could instead display a help page.

It is good practice to include `if __name__ == "__main__"`. It makes your code more modular, helps
with readability, leaves no global variables, and avoids unintended execution.

And well everybody, that is the purpose of `if __name__ == "__main__"` in Python.

---

Hey, what's going on everybody? So in this video, we're going to create a very simple banking program
using Python. This is meant to be more of an exercise to get us used to working with functions.

When creating a project, I like to divide that project into smaller sections, then handle them one at a
time. So, we'll do that by declaring all the functions we'll need first.

With a banking program, we'll need to show a user their balance. We'll define a function to show
balance. For the time being, I'll write `pass` just as a placeholder.

We'll need to make a deposit. Define `deposit`.

Make a withdrawal. Define `withdraw`.

Near the end of this project, we will be creating a main function and placing the main body of our code
within it. We'll handle that near the end just to contain everything.
We have our three functions with our banking program. We'll need to show a balance, make a deposit,
or make a withdrawal.

What are some variables we'll need? Well, we'll need a balance, which I will set to be zero initially. I will
also create a boolean of `is_running`. This will be true. If at any time we set `is_running` to be false, we'll
exit the program.

So with the majority of our code, we'll place it within a while loop:

```python

while is_running:

# You can check to see if this is equal to True, but since this is a boolean, that's not necessary.

print("Banking Program")

print("1. Show balance")

print("2. Make a deposit")

print("3. Make a withdrawal")

print("4. Exit")

choice = input("Enter your choice: ")

if choice == "1":

show_balance()

elif choice == "2":

deposit()

elif choice == "3":

withdraw()

elif choice == "4":

is_running = False

else:

print("Invalid choice. Please try again.")

```

Within each function, we will handle the respective functionality.


This is a great exercise to get used to working with functions and control flow.

---

That concludes the explanation.

```python

def show_balance(balance):

print("\n" + "*" * 30)

print(f"Your balance is ${balance:.2f}")

print("*" * 30 + "\n")

def deposit():

amount = float(input("Enter an amount to be deposited: "))

if amount <= 0:

print("That's not a valid amount.")

return 0

else:

return amount

def withdraw(balance):

amount = float(input("Enter amount to be withdrawn: "))

if amount > balance:

print("Insufficient funds.")

return 0

elif amount <= 0:

print("Amount must be greater than zero.")

return 0

else:

return amount

def main():

balance = 0.0
is_running = True

while is_running:

print("\n" + "*" * 30)

print("Banking Program")

print("1. Show balance")

print("2. Make a deposit")

print("3. Make a withdrawal")

print("4. Exit")

print("*" * 30)

choice = input("Enter your choice (1-4): ")

if choice == "1":

show_balance(balance)

elif choice == "2":

amount = deposit()

balance += amount

elif choice == "3":

amount = withdraw(balance)

balance -= amount

elif choice == "4":

is_running = False

else:

print("That is not a valid choice.")

print("\nThank you. Have a nice day.")

if __name__ == "__main__":

main()

```

```python

# Win. Okay, see I got all bells. It says you won $10.
# Once somebody runs out of money, we want to stop them from playing or if they would like to exit.

play_again = input("Do you want to spin again? (Y/N): ").upper()

if play_again != "Y":

break

# Test run:

bet = float(input("Enter your bet amount: "))

play_again = input("Do you want to spin again? (Y/N): ").upper()

if play_again != "Y":

break

# If lowercase y is typed, convert to uppercase to register:

play_again = input("Do you want to spin again? (Y/N): ").upper()

# Display current balance and game over message:

print(f"Game over. Your final balance is ${balance}")

# Slot machine program summary:

# - User places bets

# - Wins or loses money based on spins

# - Can choose to play again or exit

# - Balance updates accordingly

# ------------------------------------------------------------

# Substitution cipher encryption program

import random

import string

# Create a string of characters to use for encryption

chars = string.punctuation + string.digits + string.ascii_letters + " "


# Convert string to list of characters

chars = list(chars)

# Create a key by copying and shuffling chars

key = chars.copy()

random.shuffle(key)

# Encrypt message

plain_text = input("Enter a message to encrypt: ")

cipher_text = ""

for letter in plain_text:

index = chars.index(letter)

cipher_text += key[index]

print(f"Original message: {plain_text}")

print(f"Encrypted message: {cipher_text}")

# Decrypt message

cipher_text_input = input("Enter a message to decrypt: ")

decrypted_text = ""

for letter in cipher_text_input:

index = key.index(letter)

decrypted_text += chars[index]

print(f"Decrypted message: {decrypted_text}")

# ------------------------------------------------------------

# Hangman game
import random

# Words list

words = ["apple", "orange", "banana", "coconut", "pineapple"]

# Hangman ASCII art dictionary

hangman_art = {

0: (" ", " ", " "),

1: (" O ", " ", " "),

2: (" O ", " | ", " "),

3: (" O ", "/| ", " "),

4: (" O ", "/|\\", " "),

5: (" O ", "/|\\", "/ "),

6: (" O ", "/|\\", "/ \\"),

def display_man(wrong_guesses):

print("*" * 10)

for line in hangman_art[wrong_guesses]:

print(line)

print("*" * 10)

def display_hint(hint):

print(" ".join(hint))

def display_answer(answer):

print(" ".join(answer))

def main():

answer = random.choice(words)

hint = ["_"] * len(answer)


wrong_guesses = 0

guessed_letters = set()

is_running = True

while is_running:

display_man(wrong_guesses)

display_hint(hint)

guess = input("Enter a letter: ").lower()

# Input validation

if len(guess) != 1 or not guess.isalpha():

print("Invalid input. Please enter a single alphabetical character.")

continue

if guess in guessed_letters:

print(f"{guess} is already guessed.")

continue

guessed_letters.add(guess)

if guess in answer:

for i in range(len(answer)):

if answer[i] == guess:

hint[i] = guess

else:

wrong_guesses += 1

# Check win condition

if "_" not in hint:

display_hint(hint)

print("Congratulations! You won!")

is_running = False
# Check lose condition

if wrong_guesses == 6:

display_man(wrong_guesses)

print(f"You lost! The word was: {answer}")

is_running = False

if __name__ == "__main__":

main()

```

```python

# We know that that works. Now, we need a win condition if we guess all of the correct characters and
display the entire word.

# We'll work on that next.

# If there are no underscore characters in our hint, this will be true.

if "_" not in hint:

display_man(wrong_guesses)

display_answer(answer)

print("You win!")

is_running = False

# Let's win this time. I already know that this word is probably pineapple.

# Let's guess something incorrect.

# There we go. We have two wrong guesses, but we have correctly guessed the word pineapple.

# You win and we exit the program.

# What if we lose? I'll add the following if statement.

elif wrong_guesses >= len(hangman_art) - 1: # hangman_art has 7 keys, so 6 is the max wrong guesses

display_man(wrong_guesses)

display_answer(answer)

print("You lose!")

is_running = False
# I'll guess incorrect letters.

# There the word was coconut.

# We have six incorrect guesses.

# We display the entire hangman.

# The correct answer was coconut.

# You lose.

# If you would like to import a larger variety of words, we could create a separate Python file for that.

# So within our project folder, we will create a new Python file.

# I will name this Python file words_list.py (all lowercase).

# This will be a Python file.

# Let's take our set of words and cut it.

# I'll add a note that these are words for hangman game.

# Words will be a set.

# I recommend looking online for a very large set of words that we can use.

# Then just copy and paste them within here.

# While browsing online, I found an extensive list of animals that I can use.

# So here are all the possible words for my game.

# From my main Python file, I have to import this module of words_list.

from words_list import words

# Now I have a greater variety of words I can use.

# Let's run this one last time.

# This word has four letters.

# I'll guess the vowels.

# There's an A. No.

# E. I.
# O. There is an O.

# Is it goat? Nope.

# T R bore.

# All right. The word was boar.

# You win.

# ------------------------------------------------------------

# Object-Oriented Programming (OOP) in Python

# An object is a bundle of related attributes and methods.

# Attributes are similar to variables to describe what the object has.

# For example, an attribute of the phone next to me could be version number = 13.

# Is_on could be another attribute (True or False).

# Price could be another attribute.

# Objects also have methods, which are functions that belong to an object.

# Methods define what the object can do.

# To create many objects, we'll need to utilize a class.

# A class is a blueprint used to design the structure and layout of an object.

# Example: Class Car

class Car:

def __init__(self, model, year, color, for_sale):

self.model = model

self.year = year

self.color = color

self.for_sale = for_sale
def drive(self):

print(f"You drive the {self.color} {self.model}")

def stop(self):

print(f"You stop the {self.color} {self.model}")

def describe(self):

print(f"{self.year} {self.color} {self.model}")

# Create car objects

car1 = Car("Mustang", 2024, "red", False)

car2 = Car("Corvette", 2025, "blue", True)

car3 = Car("Charger", 2026, "yellow", True)

# Access attributes and methods

print(car1.model) # Mustang

print(car1.year) # 2024

print(car1.color) # red

print(car1.for_sale) # False

car1.drive() # You drive the red Mustang

car1.stop() # You stop the red Mustang

car1.describe() # 2024 red Mustang

car2.drive() # You drive the blue Corvette

car2.stop() # You stop the blue Corvette

car2.describe() # 2025 blue Corvette

car3.drive() # You drive the yellow Charger

car3.stop() # You stop the yellow Charger

car3.describe() # 2026 yellow Charger


# ------------------------------------------------------------

# Class Variables in Python

class Student:

class_year = 2024 # Class variable shared by all instances

num_students = 0 # Class variable to count number of students

def __init__(self, name, age):

self.name = name # Instance variable unique to each object

self.age = age # Instance variable unique to each object

Student.num_students += 1 # Increment class variable on each new instance

# Create student objects

student1 = Student("Spongebob", 30)

student2 = Student("Patrick", 35)

student3 = Student("Squidward", 55)

student4 = Student("Sandy", 27)

# Access instance variables

print(student1.name, student1.age) # Spongebob 30

print(student2.name, student2.age) # Patrick 35

# Access class variables (best practice: via class name)

print(Student.class_year) # 2024

print(Student.num_students) # 4

# Print summary using f-string

print(f"My graduating class of {Student.class_year} has {Student.num_students} students.")

# Print names of all students

print(student1.name)
print(student2.name)

print(student3.name)

print(student4.name)

# ------------------------------------------------------------

# Inheritance in Python

class Animal:

def __init__(self, name=None):

self.name = name

self.is_alive = True

def eat(self):

print(f"{self.name} is eating.")

def sleep(self):

print(f"{self.name} is sleeping.")

class Dog(Animal):

pass

class Cat(Animal):

pass

class Mouse(Animal):

pass

# Create objects

dog = Dog("Scooby")

cat = Cat("Garfield")

mouse = Mouse("Mickey")
# Access attributes and methods inherited from Animal

print(dog.name) # Scooby

print(dog.is_alive) # True

dog.eat() # Scooby is eating.

dog.sleep() # Scooby is sleeping.

print(cat.name) # Garfield

print(cat.is_alive) # True

cat.eat() # Garfield is eating.

cat.sleep() # Garfield is sleeping.

print(mouse.name) # Mickey

print(mouse.is_alive) # True

mouse.eat() # Mickey is eating.

mouse.sleep() # Mickey is sleeping.

# ------------------------------------------------------------

# Note: If you need to modify a method for a child class, you can override it by defining it in the child
class.

# This avoids duplicating code and keeps your program maintainable.

```

It's not too bad if you only have a few classes, but imagine if you have hundreds of classes. That's going
to take a lot of work. It's a lot easier to write the code once and then reuse it. And I only need to make
that change in one place rather than make that change many times.

So, let's change **is sleeping** to **is asleep** and see if that works again.

```python

Mickey is asleep.

```
Let's replace mouse with dog.

```python

Scooby is asleep.

```

Not only that, but with children classes, they can have their own attributes and methods that are
different from one another. So, dogs have all these attributes and methods and they can speak. Let's
create a `speak` method. And I will print a unique message for dogs:

```python

def speak(self):

print("Woof")

```

Cats will also have a `speak` method, but it's going to be different. Cats will meow:

```python

def speak(self):

print("Meow")

```

Then for our mouse class, they will squeak:

```python

def speak(self):

print("Squeak")

```

Let's have our dog speak:

```python

dog.speak() # Woof
```

Let's have our cat speak:

```python

cat.speak() # Meow

```

And our mouse:

```python

mouse.speak() # Squeak

```

All right, everybody. So, that's an introduction to inheritance. Inheritance allows a class to inherit
attributes and methods from another class. Much like in real life, a child can inherit traits from a parent.
These are also known as sub and superclasses which is a topic for another day.

Inheritance helps with code reusability and extensibility. If all of these children classes inherit these
attributes and methods from another class, we only need to write that code once and not copy it for
every single class that needs it. We can write and change the code in one place for better reusability and
extensibility.

And well everybody, that is an introduction to inheritance in Python.

---

Hey everybody. So today we got to talk about both **multiple** and **multi-level inheritance**.

We'll begin with **multiple inheritance**. That's when a child class inherits from more than one parent
class. For example, a class `C` can inherit the traits from both class `A` and `B`. In Python, you can have
more than one parent. Multi-level inheritance we'll talk about near the end of this topic.
So in this example, we're going to create two parent classes: `Prey` and `Predator`. We'll create a class of
`Rabbit`, a class of `Hawk`, then class `Fish`. `Rabbit`, `Hawk`, and `Fish` are going to be children classes.
`Prey` and `Predator` will be parents.

If one of these classes, `Rabbit`, `Hawk`, or `Fish`, inherit from `Prey`, they get the ability to flee. We will
define a method `flee`:

```python

def flee(self):

print("This animal is fleeing.")

```

If you're a predator, you get the method to hunt. Define `hunt`:

```python

def hunt(self):

print("This animal is hunting.")

```

Rabbits will inherit from the `Prey` class. They're typically not predators except that one rabbit in Monty
Python and the Holy Grail. That's the exception. `Rabbit` will inherit the `Prey` class. Then it gets access
to a `flee` method.

Hawks are predators. They will inherit the `Predator` class.

Now fish, they will hunt smaller fish and flee from bigger fish. You can consider fish both prey and
predators. So they will inherit both classes. We will use multiple inheritance. They will inherit everything
from the `Prey` class and the `Predator` class.

Now let's see if this does in fact work.

We'll create a `Rabbit` object:

```python

rabbit = Rabbit()
```

There are no parameter setup. We don't need to send any arguments to the constructor.

```python

hawk = Hawk()

fish = Fish()

```

So, let's take our `rabbit` object and they should have a `flee` method:

```python

rabbit.flee() # This animal is fleeing.

```

But they do not have a `hunt` method because they're not predators:

```python

rabbit.hunt() # AttributeError: 'Rabbit' object has no attribute 'hunt'

```

Hawks can hunt. They're predators. They inherited that method:

```python

hawk.hunt() # This animal is hunting.

```

But they can't flee. They're not prey:

```python

hawk.flee() # AttributeError: 'Hawk' object has no attribute 'flee'

```
Fish can do both. They inherit from the `Prey` class and the `Predator` class:

```python

fish.flee() # This animal is fleeing.

fish.hunt() # This animal is hunting.

```

Children classes can inherit from more than one parent, which is what we did for fish. They are both
prey and predators. Whereas rabbits are just prey, hawks are just predators.

If you need to inherit from more than one parent, you just add that additional class to the inheritance
list.

With **multi-level inheritance**, a parent can inherit from another parent. We will create a class of
`Animal`. And for now, I'll write `pass`.

`Prey` and `Predator` are going to inherit from the `Animal` class. So, we need to add `Animal` to each
inheritance list.

Let's say if you're an animal, you get a method to eat. All animals will eat:

```python

def eat(self):

print("This animal is eating.")

```

And you can sleep:

```python

def sleep(self):

print("This animal is sleeping.")

```
So think of `Rabbit`, `Hawk`, and `Fish` as children classes. `Prey` and `Predator` are those classes'
parents, and `Animal` is the grandparent.

`Prey` and `Predator` will inherit everything that the `Animal` class has. `Rabbit`, `Hawk`, and `Fish` will
inherit everything the `Prey` and `Predator` classes have.

So now our `Rabbit`, `Hawk`, and `Fish` classes should have the ability to eat and sleep. And we'll test
that:

```python

rabbit.eat() # This animal is eating.

rabbit.sleep() # This animal is sleeping.

```

Let's check out fish:

```python

fish.eat() # This animal is eating.

fish.sleep() # This animal is sleeping.

```

Okay, we're going to expand upon our example a little bit.

Each of our objects is going to have a name. Our rabbit will have a first name of Bugs. Hawk will be Tony
as in Tony Hawk. Our fish will be Nemo.

Within our classes, we don't have any constructor set up. In which class should we assign the `name`
attribute? Let's do so within our `Animal` class.

So, we will define a constructor to assign these attributes. We will receive a name. We'll assign:

```python

self.name = name

```
Now, with these other classes, if you're not assigning any attributes or if you don't need any other
initialization logic, you don't need a constructor. We'll implicitly use the constructor we inherit from the
parent.

Let's convert each of these print statements to an f-string. Replace "animal" with `self.name`.

Now let's have our rabbit use the `eat` method:

```python

rabbit.eat() # Bugs is eating.

rabbit.sleep() # Bugs is sleeping.

rabbit.flee() # Bugs is fleeing.

```

Let's check out our hawk. Hawks don't have a `flee` method because they're predators, not prey.

Let's eat:

```python

hawk.eat() # Tony is eating.

hawk.sleep() # Tony is sleeping.

hawk.hunt() # Tony is hunting.

```

Let's check our fish next. Our fish can eat:

```python

fish.eat() # Nemo is eating.

fish.sleep() # Nemo is sleeping.

fish.flee() # Nemo is fleeing.

fish.hunt() # Nemo is hunting.

```
Okay, everybody. That is both multiple and multi-level inheritance.

With multiple inheritance, a child can inherit from more than one parent class. You just add each
additional class to the inheritance list.

With multi-level inheritance, a child can inherit from a parent which inherits from another parent. Class
`C` can inherit from `B` where class `B` inherits from `A`. Think of `C` as the child, `B` is the parent and `A`
as the grandparent. `C` will have all the attributes and methods even available within the grandparent
class of `A`.

And well everybody that is both multiple and multi-level inheritance in Python.

---

Hey everybody. So today I got to talk about the **super** function in Python.

`super` is a function. It's used within a child class to call methods from a parent class. The child class is
the subclass. The parent class is the superclass. Hence why this function is named the super function.

Using the super function, it allows you to extend the functionality of the inherited methods.

Here's an example. We'll create a few shape objects. We'll need to set up the classes though. We'll have
class `Circle`. For the time being, I'll just write `pass`. We'll fill it in later. Class `Square` and class
`Triangle`.

For each of these classes, in order to instantiate objects, we'll need a constructor. We will define our
constructor, our `__init__` method.

When creating circles, what sorts of attributes should a circle have? Let's say a color. What's the color of
the circle? Is it filled or not? `filled` will be another attribute and a radius. Then let's assign these:

```python

self.color = color

self.filled = filled
self.radius = radius

```

Let's do this with the square and triangle. Really, I'll just copy our constructor and paste it.

Squares don't have a radius. With a square, the width and the height are the same. Let's replace radius
with width. We'll also keep the color and filled attributes:

```python

self.width = width

```

Now, with triangles again, let's copy our constructor. We'll need a width and a height:

```python

self.width = width

self.height = height

```

So with programming, we try not to repeat ourselves if we don't have to. What do all of these classes
have in common? They all share the attributes of color and filled. The ways in which they are different is
that circle has a radius attribute, square has a width, triangle has a width and a height.

If we have to make any changes to one of these attributes, we would have to do so manually. For
example, let's replace `filled` with `is_filled`. Now, I need to look throughout my code for any instance of
`filled` and replace it with `is_filled`. It's a lot of work and I might make a mistake such as here and here.

It's better to write your code once and try and reuse it. So, that's where inheritance and the super
function can come in handy.

We're going to take the attributes of color and is_filled and place it within a parent class. These children
classes will inherit those attributes.

So:
```python

class Shape:

def __init__(self, color, is_filled):

self.color = color

self.is_filled = is_filled

```

`Circle` is going to inherit from its parent of `Shape`. That also applies with `Square` and `Triangle`.

We'll set up a constructor for `Shape`:

```python

def __init__(self, color, is_filled):

self.color = color

self.is_filled = is_filled

```

We don't need to manually assign these attributes within each of these constructors for the children.
Instead what we have to do is within the constructor for each of these children classes we have to call
the constructor for the parent, also known as the superclass of `Shape`.

So we will eliminate these two lines of code. Use the super function, call the constructor of the parent,
that is the `__init__` method. But we need to pass in the color that we receive and is_filled. This will be
a boolean.

And let's do this with the `Square` class and the `Triangle` class.

We still need radius for the circle, width for the square, width and height for the triangle.

We're going to call the super function to take care of whatever attributes all these types of shapes have
in common such as color and is_filled.

Now let's see if this works.


Let's construct a few objects.

We will create a circle named `circle` called a constructor for circle. We have to pass in a color, a
boolean if it's filled or not, and a radius.

So for the color of the circle, let's say red, is_filled is true, and a radius of five.

You could even use keyword arguments for better readability. Although not necessary, but for clarity:

```python

circle = Circle(color="red", is_filled=True, radius=5)

```

Let's see if this works.

I will print our circle's color:

```python

print(circle.color) # red

```

Print our circle's is_filled attribute:

```python

print(circle.is_filled) # True

```

And the radius:

```python

print(f"{circle.radius} cm") # 5 cm

```
Let's construct a square object:

```python

square = Square(color="blue", is_filled=False, width=6)

```

We don't need a height because squares have an even width and height. If we ever need the height, we
can assume it's the same as the width. In this case, 6.

Let's check out our square:

```python

print(square.color) # blue

print(square.is_filled) # False

print(f"{square.width} cm") # 6 cm

```

Let's create a triangle object:

```python

triangle = Triangle(color="yellow", is_filled=True, width=7, height=8)

```

Let's print our triangle's color, is_filled, width, and height:

```python

print(triangle.color) # yellow

print(triangle.is_filled) # True

print(f"{triangle.width} cm") # 7 cm

print(f"{triangle.height} cm") # 8 cm

```
So that's how you can use the super function to reuse the constructor of a parent class. We don't need
to manually assign each of these attributes within each of the children classes. We can do that in just
one place.

When we refer to `super`, imagine that we're replacing this with the parent class name such as `Shape`.
That might be a good way to think of it. Use the constructor of the parent class of `Shape` and pass
these arguments in.

What you could do as well is extend the functionality of a method.

So within our `Shape` class, let's create a method of `describe`. We will describe the attributes of this
shape. We will print using an f-string:

```python

def describe(self):

print(f"It is {self.color} and {'filled' if self.is_filled else 'not filled'}.")

```

Each of these types of shapes `Circle`, `Square`, and `Triangle` will have access to a `describe` method.

Let's attempt to use it.

Take our circle, use the `describe` method that's inherited:

```python

circle.describe() # It is red and filled.

square.describe() # It is blue and not filled.

triangle.describe() # It is yellow and filled.

```

So then we also have **method overriding**.

What if we create a similar method of `describe` within `Circle`, `Square`, and `Triangle`?
Let's do that.

Define a `describe` method within our `Circle`:

```python

def describe(self):

area = 3.14 * self.radius ** 2

print(f"It is a circle with an area of {area} cm squared.")

```

If I were to call the `describe` method, will we use the parent's version of `describe` or the child's?

So let's take our circle, use the `describe` method:

```python

circle.describe() # It is a circle with an area of 78.5 cm squared.

```

This is called **method overriding**. If a child shares a similar method with a parent, you'll use the
child's version and not the parent's.

If you would like to extend the functionality of a method from a parent, you can use the super function.

Not only do I want to use the `describe` method of the child, I would also like to use the `describe`
method of the parent.

So within this function we will use the super function to access the `describe` method of the parent.

What we're doing is extending the functionality of the `describe` method.

```python

def describe(self):

area = 3.14 * self.radius ** 2


print(f"It is a circle with an area of {area} cm squared.")

super().describe()

```

It is a circle with an area of 78.5 cm squared. The circle is red and it's filled.

Or you could change up the order:

```python

def describe(self):

super().describe()

area = 3.14 * self.radius ** 2

print(f"It is a circle with an area of {area} cm squared.")

```

Let's finish this with the `Square` and `Triangle` classes.

I'll copy what we have for the `describe` method within the `Circle` class, but we'll make a different
calculation.

Describe the square:

```python

def describe(self):

area = self.width * self.width

print(f"It is a square with an area of {area} cm squared.")

super().describe()

```

The height and the width are going to be the same if it's a square.

Then describe our triangle:


```python

def describe(self):

area = (self.width * self.height) / 2

print(f"It is a triangle with an area of {area} cm squared.")

super().describe()

```

We've already described our circle. Let's describe our square:

```python

square.describe()

# It is a square with an area of 36 cm squared.

# It is blue and not filled.

```

Let's describe our triangle:

```python

triangle.describe()

# It is a triangle with an area of 28.0 cm squared.

# It is yellow and filled.

```

All right, everybody. That is the super function. It's used in a child class to call the methods from a
parent class, also known as the superclass. It allows you to extend the functionality of the inherited
methods within a child class.

You could use it within a constructor to assign any attributes that all of its siblings have in common, such
as color or if that shape is filled.

When used within any other method, you can extend the functionality of that method. Not only are we
printing this message from the parent, we're tacking on another print statement before that.

And well everybody that is the super function in Python.


---

What is going on everybody? So today I got to talk about **polymorphism** in Python.

Polymorphism is a programming concept. It's a Greek word that means to have many forms or faces.
Poly means many. Morph means form.

In programming, an object can take one of many forms.

There's two ways to achieve polymorphism. One is through inheritance. An object could be treated as
the same type as a parent class. There's also duck typing, which we'll talk about in the next topic.

In this video, we're more focused on inheritance.

What we'll do in this video is create a class of `Shape`. We'll write `pass` as a placeholder.

We will create a class of `Circle` which will inherit from `Shape`. Again writing `pass`.

Class `Square` inherits from `Shape`.

Class `Triangle` which inherits from `Shape`.

If I was to create a circle object:

```python

circle = Circle()

```

Our circle identifies as a circle. And since our circle class inherits from the shape class, our circle is also
considered a shape. It has two forms. It's a circle and it's a shape.

But our circle isn't a square or a triangle.


That could also apply to our square class. Our square is a square. Our square is also considered a shape.
But our square is not a circle or a triangle.

Those are two possible forms for our square. It's a square and a shape.

So let's say we would like to create a list of shapes.

What do they all have in common? Well, they're all shapes.

A descriptive name for this list would be:

```python

shapes = []

```

I will instantiate a circle object, a square object, and a triangle object.

Our circle is a circle and a shape.

Our square is a square and a shape.

Our triangle is a triangle and a shape.

Each of these objects has two forms or two faces.

Let's fill in some of these classes.

Let's say that with our `Shape` class, we will define an `area` method:

```python

def area(self):

pass
```

I'm going to turn this into an abstract method.

I'll just write `pass`.

To work with abstract classes, we need to import that from `abc`:

```python

from abc import ABC, abstractmethod

```

Preceding the `area` method, I will add a decorator of `@abstractmethod`.

Our `Circle`, `Square`, and `Triangle` classes, they're all considered shapes. They inherit from this class.

We need to define an `area` method for each since they're all considered a shape. Every shape has an
area.

With our class of `Circle`, let's define a constructor:

```python

def __init__(self, radius):

self.radius = radius

```

Let's do this with `Square`:

```python

def __init__(self, side):

self.side = side

```
Then `Triangle`:

```python

def __init__(self, base, height):

self.base = base

self.height = height

```

All right.

Now let's finish defining these `area` methods for each class.

We will return:

```python

3.14 * self.radius ** 2

```

So given a radius, that's how to calculate the area of a circle.

Then with our square:

```python

return self.side ** 2

```

Then with our triangle:

```python

return (self.base * self.height) / 2

```
Now we have to pass in some arguments.

For our circle, we need a radius. I'll pick 4.

For the square, the length of a side will be 5.

Then our triangle, the base will be 6. The height will be 7.

We're going to write a loop to iterate through our shapes:

```python

for shape in shapes:

print(f"{shape.area()} cm squared")

```

And that would give me these numbers.

If you would like, you can format the output. I'll just use an f-string. I'll add cm squared.

Much better.

What if we were to create a class that's completely unrelated to shapes?

I will create a class of `Pizza`.

I will define a constructor.

To construct a pizza object, we need a topping and a radius. What is the radius of the pizza?

```python

self.author

```
What if the key is number of pages?

```python

elif key == "num_pages":

return self.num_pages

```

If the key is something else, we can raise a KeyError:

```python

else:

raise KeyError(f"{key} is not a valid key")

```

Now we can access attributes by indexing:

```python

print(book1["title"]) # The Hobbit

print(book2["author"]) # JK Rowling

print(book3["num_pages"]) # 172

```

If we try an invalid key:

```python

print(book1["publisher"]) # Raises KeyError: publisher is not a valid key

```

---

# Summary of Magic Methods Covered


- `__init__(self, ...)`: Constructor, called when creating an object.

- `__str__(self)`: String representation, used by `print()`.

- `__eq__(self, other)`: Equality comparison `==`.

- `__lt__(self, other)`: Less than `<`.

- `__gt__(self, other)`: Greater than `>`.

- `__add__(self, other)`: Addition `+`.

- `__contains__(self, keyword)`: Membership test `in`.

- `__getitem__(self, key)`: Indexing `obj[key]`.

These magic methods allow customizing how objects behave with built-in Python operations.

---

Feel free to ask if you'd like me to provide the full class code with all these magic methods
implemented!

```python

def __getitem__(self, key):

if key == "author":

return self.author

elif key == "num_pages":

return self.num_pages

else:

return f"'{key}' was not found"

# Example usage:

book1 = Book("The Hobbit", "JRR Tolkien", 310)

book2 = Book("Harry Potter", "JK Rowling", 223)

book3 = Book("The Lion, the Witch and the Wardrobe", "CS Lewis", 172)

print(book3["author"]) # CS Lewis

print(book2["author"]) # JK Rowling

print(book1["author"]) # JRR Tolkien


print(book1["num_pages"]) # 310

print(book2["num_pages"]) # 223

print(book3["num_pages"]) # 172

print(book3["audio"]) # 'audio' was not found

```

---

Magic methods, also known as dunder methods (double underscore), are special methods in Python
that are automatically called by many of Python's built-in operations. They allow developers to define or
customize the behavior of objects when using those built-in operations.

---

# Property Decorator in Python

The property decorator allows us to define a method as a property, so it can be accessed like an
attribute. It provides getter, setter, and deleter methods to add additional logic when reading, writing,
or deleting attributes.

Example:

```python

class Rectangle:

def __init__(self, width, height):

self._width = width # Private attribute

self._height = height # Private attribute

@property

def width(self):

return f"{self._width:.1f} cm"

@width.setter
def width(self, new_width):

if new_width > 0:

self._width = new_width

else:

print("Width must be greater than zero.")

@width.deleter

def width(self):

del self._width

print("Width has been deleted.")

@property

def height(self):

return f"{self._height:.1f} cm"

@height.setter

def height(self, new_height):

if new_height > 0:

self._height = new_height

else:

print("Height must be greater than zero.")

@height.deleter

def height(self):

del self._height

print("Height has been deleted.")

# Usage

rectangle = Rectangle(3, 4)

print(rectangle.width) # 3.0 cm

print(rectangle.height) # 4.0 cm
rectangle.width = 5 # Sets width to 5

rectangle.height = -1 # Prints warning, height unchanged

del rectangle.width # Deletes width attribute

del rectangle.height # Deletes height attribute

```

---

# Decorators in Python

A decorator is a function that extends the behavior of another function without modifying the base
function. It takes the base function as an argument and returns a new function that adds extra behavior.

Example:

```python

def add_sprinkles(func):

def wrapper(*args, **kwargs):

print("You add sprinkles ")

return func(*args, **kwargs)

return wrapper

def add_fudge(func):

def wrapper(*args, **kwargs):

print("You add fudge ")

return func(*args, **kwargs)

return wrapper

@add_sprinkles

@add_fudge

def get_ice_cream(flavor):
print(f"Here is your {flavor} ice cream ")

get_ice_cream("vanilla")

# Output:

# You add sprinkles

# You add fudge

# Here is your vanilla ice cream

```

---

# Exception Handling in Python

Exceptions are events that interrupt the normal flow of a program. Common exceptions include
`ZeroDivisionError`, `TypeError`, and `ValueError`.

Use `try`, `except`, and `finally` blocks to handle exceptions gracefully:

```python

try:

number = int(input("Enter a number: "))

print(1 / number)

except ZeroDivisionError:

print("You can't divide by zero, idiot.")

except ValueError:

print("Enter only numbers, please.")

except Exception as e:

print(f"Something went wrong: {e}")

finally:

print("Do some cleanup here.")

```
---

# File Detection with `os` Module

To check if a file exists, use the `os.path.exists()` method:

```python

import os

file_path = "test.txt" # Relative path

if os.path.exists(file_path):

print(f"The location '{file_path}' exists.")

else:

print(f"The location '{file_path}' does not exist.")

```

For files inside folders, include the folder in the path:

```python

file_path = "stuff/test.txt"

```

For absolute paths, use the full path string.

---

If you want me to provide code examples or explanations for any specific part, just ask!

```python

import os

# Absolute file path example with escape sequence issue


file_path = "C:\\Users\\YourName\\Documents\\test.txt" # Use double backslashes

# Or use forward slashes

file_path = "C:/Users/YourName/Documents/test.txt"

# Check if the file exists

if os.path.exists(file_path):

print(f"The location of that absolute file path does exist.")

# Check if it is a file

if os.path.isfile(file_path):

print("That is a file.")

# Check if it is a directory

elif os.path.isdir(file_path):

print("That is a directory.")

else:

print("That location does not exist.")

# Writing and outputting files using Python

# Plain text example

text_data = "I like pizza"

file_path = "output.txt" # Relative path

with open(file_path, mode="w") as file:

file.write(text_data)

print(f"Text file '{file_path}' was created.")

# Absolute path example (adjust path accordingly)

abs_path = "C:/Users/YourName/Desktop/output.txt"

with open(abs_path, mode="w") as file:

file.write(text_data)
print(f"Text file '{abs_path}' was created.")

# Handling file exists error with mode 'x'

try:

with open(abs_path, mode="x") as file:

file.write(text_data)

except FileExistsError:

print("That file already exists.")

# Appending to a file

with open(file_path, mode="a") as file:

file.write("\n" + text_data)

# Writing a list of employees to a file

employees = ["Eugene", "Squidward", "Spongebob", "Patrick"]

with open("employees.txt", mode="w") as file:

for employee in employees:

file.write(employee + "\n")

# Writing JSON file

import json

employee = {

"name": "Spongebob",

"age": 30,

"job": "cook"

json_path = "employee.json"

with open(json_path, mode="w") as file:

json.dump(employee, file, indent=4)


print(f"JSON file '{json_path}' was created.")

# Writing CSV file

import csv

employees_data = [

["name", "age", "job"],

["Spongebob", 30, "cook"],

["Patrick", 37, "unemployed"],

["Sandy", 27, "scientist"]

csv_path = "employees.csv"

with open(csv_path, mode="w", newline="") as file:

writer = csv.writer(file)

for row in employees_data:

writer.writerow(row)

print(f"CSV file '{csv_path}' was created.")

# Reading files using Python

# Reading plain text file

file_path = "input.txt" # Adjust path accordingly

try:

with open(file_path, mode="r") as file:

content = file.read()

print(content)

except FileNotFoundError:

print("File was not found.")


except PermissionError:

print("You do not have permission to read that file.")

# Reading JSON file

json_path = "input.json"

import json

try:

with open(json_path, mode="r") as file:

content = json.load(file)

print(content)

print(content.get("name"))

except FileNotFoundError:

print("JSON file not found.")

except json.JSONDecodeError:

print("Error decoding JSON.")

# Reading CSV file

csv_path = "input.csv"

import csv

try:

with open(csv_path, mode="r") as file:

reader = csv.reader(file)

for line in reader:

print(line)

except FileNotFoundError:

print("CSV file not found.")

# Working with dates and times using Python

import datetime
# Create a date object

date_obj = datetime.date(2025, 1, 2)

print(date_obj) # 2025-01-02

# Get today's date

today = datetime.date.today()

print(today) # e.g., 2024-07-14

# Create a time object

time_obj = datetime.time(12, 30, 0)

print(time_obj) # 12:30:00

# Get current date and time

now = datetime.datetime.now()

print(now) # e.g., 2024-07-14 09:00:00.123456

# Format datetime string

formatted_now = now.strftime("%H:%M:%S %m-%d-%Y")

print(formatted_now) # e.g., 09:00:00 07-14-2024

# Check if target date/time has passed

target_datetime = datetime.datetime(2030, 1, 2, 12, 30, 1)

current_datetime = datetime.datetime.now()

if target_datetime < current_datetime:

print("Target date has passed.")

else:

print("Target date has not passed.")

```

```python

import time
import datetime

import pygame

def set_alarm(alarm_time):

print(f"Alarm set for {alarm_time}")

sound_file = "my_music.mp3" # Replace with your MP3 file name

pygame.mixer.init()

pygame.mixer.music.load(sound_file)

is_running = True

while is_running:

current_time = datetime.datetime.now().strftime("%H:%M:%S")

print(current_time)

if current_time == alarm_time:

print("Wake up! ")

pygame.mixer.music.play()

while pygame.mixer.music.get_busy():

time.sleep(1)

is_running = False

time.sleep(1)

if __name__ == "__main__":

alarm_time = input("Enter alarm time (HH:MM:SS) in 24-hour format: ")

set_alarm(alarm_time)

```

---

```python

import threading

import time
def walk_dog(name):

time.sleep(8)

print(f"You finish walking {name}.")

def take_out_trash():

time.sleep(2)

print("You take out the trash.")

def get_mail():

time.sleep(4)

print("You get the mail.")

chore1 = threading.Thread(target=walk_dog, args=("Scooby",))

chore2 = threading.Thread(target=take_out_trash)

chore3 = threading.Thread(target=get_mail)

chore1.start()

chore2.start()

chore3.start()

chore1.join()

chore2.join()

chore3.join()

print("All chores are complete.")

```

---

```python

import requests
def get_pokemon_info(name):

base_url = "https://pokeapi.co/api/v2/pokemon/"

url = f"{base_url}{name.lower()}"

response = requests.get(url)

if response.status_code == 200:

print("Data retrieved")

return response.json()

else:

print(f"Failed to retrieve data. Status code: {response.status_code}")

return None

if __name__ == "__main__":

pokemon_name = input("Enter a Pokémon name: ")

pokemon_info = get_pokemon_info(pokemon_name)

if pokemon_info:

print(f"Name: {pokemon_info['name'].capitalize()}")

print(f"ID: {pokemon_info['id']}")

print(f"Height: {pokemon_info['height']}")

print(f"Weight: {pokemon_info['weight']}")

```

---

```python

import sys

from PyQt5.QtWidgets import QApplication, QMainWindow

class MainWindow(QMainWindow):

def __init__(self):

super().__init__()

self.setWindowTitle("My Cool First GUI")


def main():

app = QApplication(sys.argv)

window = MainWindow()

window.show()

sys.exit(app.exec_())

if __name__ == "__main__":

main()

```

```python

# GUI guey. And now we have a new title: My Cool First Guey Graphical User Interface.

# When this window appears, we can set the geometry of where the window appears and the size of the
window.

# Access self.setGeometry method. There's four arguments: X and Y for the coordinates, width, and
height.

self.setGeometry(0, 0, 500, 500)

# This sets the window to appear at the top-left corner (0,0) with width and height 500 pixels each.

# If we set X to 100, the window moves right by 100 pixels.

self.setGeometry(700, 300, 500, 500)

# This roughly centers the window on the screen (adjust as needed).

# To set a window icon, first import QIcon:

from PyQt5.QtGui import QIcon

# Then set the window icon after setting geometry:

self.setWindowIcon(QIcon("profile_pic.jpg"))

# Replace "profile_pic.jpg" with your image filename in the project folder.

# --- Labels in PyQt5 ---


from PyQt5.QtWidgets import QLabel

from PyQt5.QtGui import QFont

# Create a label in the main window constructor or initUI method:

self.label = QLabel("Hello", self)

# Set font:

self.label.setFont(QFont("Arial", 40))

# Set geometry (position and size):

self.label.setGeometry(0, 0, 500, 100)

# Set stylesheet for color and background:

self.label.setStyleSheet("""

color: #333333;

background-color: #0000FF;

font-weight: bold;

font-style: italic;

text-decoration: underline;

""")

# --- Label alignment ---

from PyQt5.QtCore import Qt

# Align text vertically top:

self.label.setAlignment(Qt.AlignTop)

# Align text vertically bottom:

self.label.setAlignment(Qt.AlignBottom)

# Align text vertically center:


self.label.setAlignment(Qt.AlignVCenter)

# Align text horizontally right:

self.label.setAlignment(Qt.AlignRight)

# Align text horizontally center:

self.label.setAlignment(Qt.AlignHCenter)

# Align text horizontally left:

self.label.setAlignment(Qt.AlignLeft)

# Combine horizontal center and vertical top:

self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)

# Shortcut for center both horizontally and vertically:

self.label.setAlignment(Qt.AlignCenter)

# --- Adding images to PyQt5 ---

from PyQt5.QtGui import QPixmap

# Create a label for the image:

self.image_label = QLabel(self)

self.image_label.setGeometry(0, 0, 250, 250)

# Load image into QPixmap:

pixmap = QPixmap("profile_pic.jpg")

# Set pixmap to label:

self.image_label.setPixmap(pixmap)

# Enable scaling of image to label size:


self.image_label.setScaledContents(True)

# Positioning the image label:

# Move to right side:

self.image_label.setGeometry(

self.width() - self.image_label.width(),

0,

self.image_label.width(),

self.image_label.height()

# Bottom right corner:

self.image_label.setGeometry(

self.width() - self.image_label.width(),

self.height() - self.image_label.height(),

self.image_label.width(),

self.image_label.height()

# Bottom left corner:

self.image_label.setGeometry(

0,

self.height() - self.image_label.height(),

self.image_label.width(),

self.image_label.height()

# Center of window (integer division for pixel coordinates):

self.image_label.setGeometry(

(self.width() - self.image_label.width()) // 2,

(self.height() - self.image_label.height()) // 2,
self.image_label.width(),

self.image_label.height()

# --- Layouts in PyQt5 ---

from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QGridLayout

def initUI(self):

central_widget = QWidget()

self.setCentralWidget(central_widget)

# Create labels with background colors:

label1 = QLabel("Number 1")

label1.setStyleSheet("background-color: red;")

label2 = QLabel("Number 2")

label2.setStyleSheet("background-color: yellow;")

label3 = QLabel("Number 3")

label3.setStyleSheet("background-color: green;")

label4 = QLabel("Number 4")

label4.setStyleSheet("background-color: blue;")

label5 = QLabel("Number 5")

label5.setStyleSheet("background-color: purple;")

# Vertical layout:

vbox = QVBoxLayout()

vbox.addWidget(label1)

vbox.addWidget(label2)

vbox.addWidget(label3)

vbox.addWidget(label4)

vbox.addWidget(label5)

central_widget.setLayout(vbox)
# For horizontal layout, replace QVBoxLayout with QHBoxLayout and set layout accordingly.

# For grid layout:

grid = QGridLayout()

grid.addWidget(label1, 0, 0)

grid.addWidget(label2, 0, 1)

grid.addWidget(label3, 1, 0)

grid.addWidget(label4, 1, 1)

grid.addWidget(label5, 2, 2)

central_widget.setLayout(grid)

# --- Push Buttons in PyQt5 ---

from PyQt5.QtWidgets import QPushButton

# In constructor or initUI:

self.button = QPushButton("Click Me", self)

self.button.setGeometry(150, 200, 200, 100)

self.button.setStyleSheet("font-size: 30px;")

# Connect button click signal to a slot method:

self.button.clicked.connect(self.on_click)

def on_click(self):

print("Button clicked")

self.button.setText("Clicked")

self.button.setDisabled(True)

# --- Label to change text on button click ---

self.label = QLabel("Hello", self)


self.label.setGeometry(150, 300, 200, 100)

self.label.setStyleSheet("font-size: 30px;")

def on_click(self):

print("Button clicked")

self.button.setText("Clicked")

self.button.setDisabled(True)

self.label.setText("Button was clicked")

```

```python

# Adjust font size of label to 50 for better visibility

self.label.setFont(QFont("Arial", 50))

# In the on_click function, update label text to "Goodbye" when button is clicked

def on_click(self):

self.label.setText("Goodbye")

# --- Checkboxes in PyQt5 ---

from PyQt5.QtWidgets import QCheckBox

from PyQt5.QtCore import Qt

# In the main window constructor:

self.checkbox = QCheckBox("Do you like food?", self)

# Initialize UI method:

self.checkbox.setStyleSheet("font-size: 30px; font-family: Arial;")

self.checkbox.setGeometry(10, 10, 500, 100)

self.checkbox.setChecked(False) # Default unchecked

# Connect signal to slot:

self.checkbox.stateChanged.connect(self.checkbox_changed)
# Define slot method:

def checkbox_changed(self, state):

if state == Qt.Checked:

print("You like food")

else:

print("You do not like food")

# --- Radio Buttons in PyQt5 ---

from PyQt5.QtWidgets import QRadioButton, QButtonGroup

# In constructor:

self.radio1 = QRadioButton("Visa", self)

self.radio2 = QRadioButton("Mastercard", self)

self.radio3 = QRadioButton("Gift Card", self)

self.radio4 = QRadioButton("In Store", self)

self.radio5 = QRadioButton("Online", self)

# Set geometry:

self.radio1.setGeometry(0, 0, 300, 50)

self.radio2.setGeometry(0, 50, 300, 50)

self.radio3.setGeometry(0, 100, 300, 50)

self.radio4.setGeometry(0, 150, 300, 50)

self.radio5.setGeometry(0, 200, 300, 50)

# Set stylesheet for all radio buttons:

self.setStyleSheet("""

QRadioButton {

font-size: 40px;

font-family: Arial;

padding: 10px;
}

""")

# Create button groups:

self.button_group1 = QButtonGroup(self)

self.button_group1.addButton(self.radio1)

self.button_group1.addButton(self.radio2)

self.button_group1.addButton(self.radio3)

self.button_group2 = QButtonGroup(self)

self.button_group2.addButton(self.radio4)

self.button_group2.addButton(self.radio5)

# Connect toggled signals to slot:

for rb in [self.radio1, self.radio2, self.radio3, self.radio4, self.radio5]:

rb.toggled.connect(self.radio_button_changed)

# Slot method:

def radio_button_changed(self):

radio_button = self.sender()

if radio_button.isChecked():

print(f"{radio_button.text()} is selected")

# --- LineEdit Widgets in PyQt5 ---

from PyQt5.QtWidgets import QLineEdit, QPushButton

# In constructor:

self.lineedit = QLineEdit(self)

self.lineedit.setGeometry(10, 10, 200, 40)

self.lineedit.setStyleSheet("font-size: 25px; font-family: Arial;")

self.lineedit.setPlaceholderText("Enter your name")


self.button = QPushButton("Submit", self)

self.button.setGeometry(210, 10, 100, 40)

self.button.setStyleSheet("font-size: 25px; font-family: Arial;")

self.button.clicked.connect(self.submit)

# Slot method:

def submit(self):

text = self.lineedit.text()

print(f"Hello {text}")

# --- Stylesheets in PyQt5 ---

from PyQt5.QtWidgets import QWidget, QHBoxLayout

# In initialize UI method:

central_widget = QWidget()

self.setCentralWidget(central_widget)

self.button1 = QPushButton("Button One")

self.button2 = QPushButton("Button Two")

self.button3 = QPushButton("Button Three")

hbox = QHBoxLayout()

hbox.addWidget(self.button1)

hbox.addWidget(self.button2)

hbox.addWidget(self.button3)

central_widget.setLayout(hbox)

# Set object names for individual styling:

self.button1.setObjectName("buttonOne")
self.button2.setObjectName("buttonTwo")

self.button3.setObjectName("buttonThree")

# Apply stylesheet to window:

self.setStyleSheet("""

QPushButton {

font-size: 40px;

font-family: Arial;

padding: 15px 75px;

margin: 25px;

border: 3px solid black;

border-radius: 15px;

QPushButton#buttonOne {

background-color: hsl(0, 100%, 50%);

QPushButton#buttonTwo {

background-color: hsl(120, 100%, 40%);

QPushButton#buttonThree {

background-color: hsl(240, 100%, 50%);

""")

```

I would say that's not too bad. If you're already familiar with CSS, we can apply pseudo-classes such as
when we hover over one of the buttons. Here's how. Let's copy these three blocks, then paste them
again. We can add CSS properties. When we hover over something, we have to use the `:hover` pseudo-
class. After the ID of each of our buttons, we will add `:hover`. We can apply the following CSS
properties when we hover over the buttons. All I'm going to do is increase the lightness, let's say by 20%
each. Then when we hover over one of the buttons, the lightness is going to change. We apply the new
CSS properties.

All right, everybody. So that is a more in-depth explanation of the `setStyleSheet` method in PyQt5.
---

All right, everybody. In today's video, we're going to build a digital clock widget using Python's PyQt5
library.

At the top of our Python file, we will need the following imports:

```python

import sys # sys means system. This module provides variables used and maintained by the Python
interpreter.

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout # Widgets are the building
blocks of a GUI application.

from PyQt5.QtCore import QTimer, QTime, Qt # QtCore provides functionality not related to GUI
components.

```

We will create a class `DigitalClock`. Instead of inheriting from the main window widget, we will inherit
from the base class `QWidget`. `QWidget` is a base class to create our own widgets. Our digital clock will
be a widget.

We will need a constructor:

```python

class DigitalClock(QWidget):

def __init__(self, *args, **kwargs):

super().__init__(*args, **kwargs)

self.initUI()

```

At the end of the constructor, I like to call a method `initUI` to initialize the user interface.

Define the `initUI` method:

```python
def initUI(self):

self.setWindowTitle("Digital Clock")

self.setGeometry(700, 300, 300, 100) # Position and size of the window

vbox = QVBoxLayout() # Vertical box layout manager

self.time_label = QLabel("12:00") # Placeholder text

self.time_label.setAlignment(Qt.AlignCenter) # Center align horizontally

# Set font size, family, and color using stylesheet

self.time_label.setStyleSheet("""

font-size: 150px;

font-family: Arial;

color: hsl(120, 100%, 50%);

""")

self.setStyleSheet("background-color: black;") # Set background color of the window

vbox.addWidget(self.time_label)

self.setLayout(vbox)

self.update_time() # Update time immediately

# Create a timer to update the time every second

self.timer = QTimer(self)

self.timer.timeout.connect(self.update_time)

self.timer.start(1000) # 1000 milliseconds = 1 second

```

Create the `update_time` method to update the label with the current time:

```python
def update_time(self):

current_time = QTime.currentTime().toString("hh:mm:ss AP") # Format with AM/PM

self.time_label.setText(current_time)

```

---

As an added bonus, if you would like to download a custom font, here's how:

1. Using Google or another search engine, look up a font of your choosing. One font I like is **DS
Digital**.

2. Download the `.ttf` (TrueType Font) file.

3. Move the `.ttf` file to your project folder (next to your main Python file).

To use the custom font in PyQt5:

```python

from PyQt5.QtGui import QFont, QFontDatabase

font_id = QFontDatabase.addApplicationFont("DS-DIGIT.TTF") # Replace with your font file name

font_family = QFontDatabase.applicationFontFamilies(font_id)[0]

my_font = QFont(font_family, 150)

self.time_label.setFont(my_font)

```

Remove the font-family from the stylesheet when using a custom font.

---

All right, everybody. So that is how to create a digital clock widget using PyQt5.

---
Hey yeah everybody. In today's video, we're going to create this stopwatch program using Python's
PyQt5 library.

You will need the following imports:

```python

import sys

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout,


QHBoxLayout

from PyQt5.QtCore import QTimer, QTime, Qt

```

We will construct a class `Stopwatch` which will inherit from the base class `QWidget`. Our stopwatch
will be a widget.

Define the constructor:

```python

class Stopwatch(QWidget):

def __init__(self, *args, **kwargs):

super().__init__(*args, **kwargs)

self.time = QTime(0, 0, 0, 0) # Hours, minutes, seconds, milliseconds

self.time_label = QLabel("00:00:00.000", self) # Placeholder text

self.start_button = QPushButton("Start", self)

self.stop_button = QPushButton("Stop", self)

self.reset_button = QPushButton("Reset", self)

self.timer = QTimer(self)
self.initUI()

```

Define the `initUI` method to design the user interface:

```python

def initUI(self):

self.setWindowTitle("Stopwatch")

vbox = QVBoxLayout()

hbox = QHBoxLayout()

vbox.addWidget(self.time_label)

hbox.addWidget(self.start_button)

hbox.addWidget(self.stop_button)

hbox.addWidget(self.reset_button)

vbox.addLayout(hbox)

self.setLayout(vbox)

self.time_label.setAlignment(Qt.AlignCenter)

# Apply stylesheet

self.setStyleSheet("""

QPushButton {

font-size: 50px;

QLabel {

font-size: 120px;

background-color: hsl(210, 100%, 50%);

border-radius: 20px;
padding: 20px;

font-weight: bold;

""")

# Connect buttons to methods

self.start_button.clicked.connect(self.start)

self.stop_button.clicked.connect(self.stop)

self.reset_button.clicked.connect(self.reset)

# Connect timer timeout to update display

self.timer.timeout.connect(self.update_display)

```

Define the methods to start, stop, reset, format time, and update display:

```python

def start(self):

self.timer.start(10) # Update every 10 milliseconds

def stop(self):

self.timer.stop()

def reset(self):

self.timer.stop()

self.time = QTime(0, 0, 0, 0)

self.update_display()

def format_time(self, time):

hours = time.hour()

minutes = time.minute()

seconds = time.second()
milliseconds = time.msec()

return f"{hours:02}:{minutes:02}:{seconds:02}.{milliseconds:03}"

def update_display(self):

self.time = self.time.addMSecs(10)

self.time_label.setText(self.format_time(self.time))

```

---

At the end of your file, add the main block to run the application:

```python

if __name__ == "__main__":

app = QApplication(sys.argv)

stopwatch = Stopwatch()

stopwatch.show()

sys.exit(app.exec_())

```

---

This will create a functional stopwatch with start, stop, and reset buttons, updating every 10
milliseconds.

Let me know if you'd like me to provide the full code or any additional explanations!

```python

# milliseconds. Now we will work on the update display method.

def update_display(self):

self.time = self.time.addMSecs(10) # Add 10 milliseconds

self.time_label.setText(self.format_time(self.time)) # Update label text

# In format_time, convert milliseconds from three digits to two by integer division by 10


def format_time(self, time):

hours = time.hour()

minutes = time.minute()

seconds = time.second()

milliseconds = time.msec() // 10 # Convert to two digits

return f"{hours:02}:{minutes:02}:{seconds:02}.{milliseconds:02}"

# Reset method to stop timer and reset time and label

def reset(self):

self.timer.stop()

self.time = QTime(0, 0, 0, 0) # Reset time to zero

self.time_label.setText(self.format_time(self.time)) # Reset label text

# Now you can start, stop, and reset the stopwatch as expected.

# ------------------------------------------------------------

# Weather App Project using PyQt5 and OpenWeatherMap API

import sys

import requests

from PyQt5.QtWidgets import (

QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout

from PyQt5.QtCore import Qt

class WeatherApp(QWidget):

def __init__(self):

super().__init__()

self.initializeUI()

def initializeUI(self):
self.setWindowTitle("Weather App")

# Create widgets

self.city_label = QLabel("Enter city name", self)

self.city_label.setObjectName("city_label")

self.city_input = QLineEdit(self)

self.city_input.setObjectName("city_input")

self.get_weather_button = QPushButton("Get Weather", self)

self.get_weather_button.setObjectName("get_weather_button")

self.get_weather_button.clicked.connect(self.get_weather)

self.temperature_label = QLabel("", self)

self.temperature_label.setObjectName("temperature_label")

self.temperature_label.setAlignment(Qt.AlignCenter)

self.emoji_label = QLabel("", self)

self.emoji_label.setObjectName("emoji_label")

self.emoji_label.setAlignment(Qt.AlignCenter)

self.description_label = QLabel("", self)

self.description_label.setObjectName("description_label")

self.description_label.setAlignment(Qt.AlignCenter)

# Layout

vbox = QVBoxLayout()

vbox.addWidget(self.city_label, alignment=Qt.AlignCenter)

vbox.addWidget(self.city_input, alignment=Qt.AlignCenter)

vbox.addWidget(self.get_weather_button)

vbox.addWidget(self.temperature_label, alignment=Qt.AlignCenter)

vbox.addWidget(self.emoji_label, alignment=Qt.AlignCenter)
vbox.addWidget(self.description_label, alignment=Qt.AlignCenter)

self.setLayout(vbox)

# Apply CSS styling

self.setStyleSheet("""

QLabel#city_label {

font-family: Calibri;

font-size: 40px;

font-style: italic;

QLineEdit#city_input {

font-size: 40px;

QPushButton#get_weather_button {

font-size: 30px;

font-weight: bold;

QLabel#temperature_label {

font-size: 75px;

QLabel#emoji_label {

font-size: 100px;

font-family: "Segoe UI Emoji";

QLabel#description_label {

font-size: 50px;

""")

def get_weather(self):

api_key = "YOUR_API_KEY_HERE" # Replace with your own API key


city = self.city_input.text()

url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}"

try:

response = requests.get(url)

response.raise_for_status() # Raise HTTPError for bad responses

data = response.json()

if data.get("cod") == 200:

self.display_weather(data)

else:

self.display_error(f"Error: {data.get('message', 'Unknown error')}")

except requests.exceptions.HTTPError as http_err:

status_code = response.status_code

match status_code:

case 400:

self.display_error("Bad request.\nPlease check your input.")

case 401:

self.display_error("Unauthorized.\nInvalid API key.")

case 403:

self.display_error("Forbidden.\nAccess is denied.")

case 404:

self.display_error("Not found.\nCity not found.")

case 500:

self.display_error("Internal server error.\nPlease try again later.")

case 502:

self.display_error("Bad gateway.\nInvalid response from server.")

case 503:

self.display_error("Service unavailable.\nServer is down.")

case 504:

self.display_error("Gateway timeout.\nNo response from server.")


case _:

self.display_error(f"HTTP error occurred:\n{http_err}")

except requests.exceptions.ConnectionError:

self.display_error("Connection error.\nCheck your internet connection.")

except requests.exceptions.Timeout:

self.display_error("Timeout error.\nThe request timed out.")

except requests.exceptions.TooManyRedirects:

self.display_error("Too many redirects.\nCheck the URL.")

except requests.exceptions.RequestException as err:

self.display_error(f"Request error:\n{err}")

def display_error(self, message):

self.temperature_label.setText("")

self.emoji_label.setText("")

self.description_label.setText(message)

def display_weather(self, data):

# Extract temperature in Kelvin and convert to Celsius

kelvin_temp = data["main"]["temp"]

celsius_temp = kelvin_temp - 273.15

self.temperature_label.setText(f"{celsius_temp:.1f} °C")

# Extract weather description and icon

description = data["weather"][0]["description"].capitalize()

icon_code = data["weather"][0]["icon"]

# Map icon code to emoji (simple example)

icon_map = {

"01d": " ", # clear sky day

"01n": " ", # clear sky night

"02d": " ", # few clouds day

"02n": " ", # few clouds night


"03d": " ", # scattered clouds

"03n": " ",

"04d": " ", # broken clouds

"04n": " ",

"09d": " ", # shower rain

"09n": " ",

"10d": " ", # rain day

"10n": " ", # rain night

"11d": " ", # thunderstorm

"11n": " ",

"13d": " ", # snow

"13n": " ",

"50d": " ", # mist

"50n": " ",

emoji = icon_map.get(icon_code, "")

self.emoji_label.setText(emoji)

self.description_label.setText(description)

if __name__ == "__main__":

app = QApplication(sys.argv)

weather_app = WeatherApp()

weather_app.show()

sys.exit(app.exec_())

```

```python

# Replace those. One thing I forgot to add, although it's not necessary, I'm going to add a colon after
each initial message.

# I think it'll look better. You don't have to do this, but I'm OCD about the appearance.

# If we encounter one of these exceptions, we'll pass along a message to our display error method and
display it within the app.
# Let's take our temperature label self.temperature_label and set the text to be our message that we
pass in.

self.temperature_label.setText(message)

# Let's do a test run. Let's look up a city that doesn't exist. Get the weather.

# So, we get that error message: Not found. City not found.

# While we're within this method, I'm going to change the font size just so that it's a little bit smaller.

# So, let's take our temperature label. Self.temperature_label.

# I'm just going to copy this because I'm lazy.

# I will call the setStyleSheet method and pass along a new property.

# Let's set the font size to 30 pixels.

self.temperature_label.setStyleSheet("font-size: 30px;")

# Let's look up North Pole. I don't think that's a city.

# Oh, I guess it is. Interesting. The North Pole is a city, I guess.

# Let's look up blah blah blah. Not found. City not found.

# Let's do another test. What if our API key is invalid?

# I'll just delete one of the digits. Let's look up Los Angeles.

# Unauthorized. Invalid API key.

# Let's change that back again.

# I will turn off my internet. Then look up Miami.

# Connection error. Check your internet connection.

# All right. So we know that our exception handling works.

# Now we're within the display_weather method.

# We'll receive an object to represent our data.

# We have to get the temperature.

# But first I'm going to print our data.


# So let me zoom in a little bit.

# Let's look up Houston. Houston, Texas.

# I need the temperature.

# Within our data object, we are looking for a key of 'main' and that is right here.

# 'main' contains a dictionary with key-value pairs.

# Once we've accessed 'main', we have to access 'temp' to get the temperature.

# And this temperature is in Kelvin.

# We'll have to convert it to Celsius or Fahrenheit. It's your choice.

# So, we need to extract this value.

# I will store it as a local variable.

temperature_K = data["main"]["temp"]

# To test it, let's print it.

print(f"Temperature in Kelvin: {temperature_K}")

# Let's convert it to Celsius and Fahrenheit.

temperature_C = temperature_K - 273.15

temperature_F = temperature_K * 9 / 5 - 459.67

# Let's print the temperature in Celsius.

print(f"Temperature in Celsius: {temperature_C:.2f}°C")

# Then in Fahrenheit.

print(f"Temperature in Fahrenheit: {temperature_F:.2f}°F")

# All right. So once we have our temperature, let's change the temperature label.

# self.temperature_label. I'll just copy this.

# Then we will set the text.


# I'll use an f-string.

# Add a placeholder.

# I'll use Fahrenheit, but feel free to use Celsius.

# Then I will add a degree symbol.

# With Windows, make sure num lock is on.

# Hold alt.

# Then on your numpad, type 0176 for a degree symbol.

# Then F for Fahrenheit.

self.temperature_label.setText(f"{temperature_F:.0f}° F")

# Let's look up Miami. Get the weather.

# And here's the temperature. 94.964.

# Now, let's say I would like no digits after the decimal.

# I can add a format specifier after our temperature.

# I'll add a colon 0F to display no decimals.

# Let's try that again.

# I will look up Miami. Get the weather.

# The current temperature in Fahrenheit is 95° F.

# Now, here's one issue.

# Let's say we display an error, then display the weather.

# If we display an error, we're going to be changing the font size.

# So, let's look up blah blah blah.

# Got the weather, city not found.

# Then, let's look up Miami again.

# Got the weather.

# And the font size is a lot smaller.

# So, if we display the weather, let's reset the font size.


# We can really just copy this line.

# So, within the display_weather method, let's set the font size back to 75, what it was originally.

self.temperature_label.setStyleSheet("font-size: 75px;")

# Let's try that again.

# Let's look up a city that doesn't exist. Get the weather.

# City not found.

# Then, we'll look up a city that does exist, like Miami, and get the weather.

# 95° F.

# Now, let's get a description of the weather.

# We'll display that at the bottom.

# In the center, we'll display a picture or an emoji, but we'll handle that last.

# Now, we need a description of the weather, like, is it sunny? Is it cloudy? Is it raining?

# So, after we calculate the temperature, so I'm going to print our data again.

# Let's look up Los Angeles.

# So currently it's 85° F.

# So for the weather description that is found at the key of 'weather'.

# We're now within a list at index zero within our list.

# We'll look up the key of 'description' which states 'clear sky'.

weather_description = data["weather"][0]["description"]

# So we will take our description label self.description_label.

# Let me just copy it.

# This one right here.

# Then we will set the text and then pass in our weather description.

self.description_label.setText(weather_description.capitalize())
# What is the weather description of Los Angeles?

# 86° F and there's a clear sky.

# Okay. Now, the last thing we're going to do is add an emoji.

# We'll add it right to the center between the temperature and the weather description.

# You don't necessarily have to, but I think it'll look cool and that's a good enough reason.

# So, let's create another method to handle that.

# We will define a method of get_weather_emoji or a picture if you would rather use a picture.

# We don't need self necessarily.

# We're going to need a weather ID.

# This method isn't going to rely on any class data or instance data.

# We could make it a static method.

# I'll add a decorator of @staticmethod.

@staticmethod

def get_weather_emoji(weather_id):

# Depending on the range of that three-digit number, we will return one of a few emojis.

if 200 <= weather_id <= 232:

return " " # Thunderstorm

elif 300 <= weather_id <= 321:

return " " # Drizzle

elif 500 <= weather_id <= 531:

return " " # Rain

elif 600 <= weather_id <= 622:

return " " # Snow

elif 701 <= weather_id <= 741:

return " " # Mist/Fog

elif weather_id == 762:

return " " # Volcano Ash

elif weather_id == 771:


return " " # Squall (violent gust of wind)

elif weather_id == 781:

return " " # Tornado

elif weather_id == 800:

return " " # Clear sky

elif 801 <= weather_id <= 804:

return " " # Clouds

else:

return "" # No emoji

# I'm going to show you where we can find that weather ID.

# I'll use a print statement.

# I will print our data.

# Let's look up Miami again.

# That's the first thing that came to mind.

# Now, at the key of 'weather', there's a key of 'id', and the value is a three-digit number.

# I'll show you this chart.

# Depending on what this three-digit number is, that corresponds to a certain group of weather.

# So the 200 range is a thunderstorm.

# 300 is a drizzle.

# 500 is rain.

# 600 is snow.

# 700 is atmosphere, like if there's a tornado or there's a volcanic eruption.

# 800 exactly is a clear sky.

# Anything that's 801 or above refers to clouds.

# So this ID is 803. We have broken clouds.

# Depending on what this ID is, I would like to return a certain emoji based on the weather.

# So we need this ID.

weather_id = data["weather"][0]["id"]
# Okay everybody, we're near the end.

# So after setting the temperature, we're going to set the emoji label.

self.emoji_label.setText(self.get_weather_emoji(weather_id))

# So now we are within our get_weather_emoji method.

# Depending on the range of that three-digit number, we will return one of a few emojis.

# We could use a match case statement.

# I think it's more complicated with the match case statement.

# We'll use else if statements for simplicity.

# So if our weather ID is greater than or equal to 200 and our weather ID is less than or equal to 232.

# Now we have two conditions here linked with the and logical operator.

# There is a shortcut to this and actually PyCharm is telling me that there is.

# We can simplify these expressions.

# Instead of two separate conditions, we can combine them into one.

# If 200 is less than or equal to our weather ID and our weather ID is less than or equal to 232,

# if this one combined condition is true, then we will return an emoji.

# So to add an emoji on Windows, you can hold down the window key and press semicolon.

# So 200 to 232, that's for a thunderstorm.

# Depending on the font style of your IDE, some of these emojis might not display properly.

# You can always just copy them from someplace else.

# I think that's better. It's more colorful.

# Then else if 300 is less than or equal to our weather ID and our weather ID is less than or equal to 321.

# This is for a partially cloudy sky.

# We will return some clouds.

# A partially cloudy sky.

# And again I don't like that one.

# So let's use this one instead.


# Else if 500 is less than or equal to our weather ID which is less than or equal to 531 we will return rain.

# Eh, that's better.

# 600 to 622

# Else if 600 is less than or equal to our weather ID which is less than or equal to 622 we will return
snow.

# So 701 to 741 is mist or fog.

# Else if 701 is less than or equal to our weather ID which is less than or equal to 741.

# We will return some mist or fog.

# 762 specifically is for ash like from a volcano.

# So else if our weather id is directly equal to 762 we will return let's return a volcano.

# 771 is for a squall that's a violent gust of wind.

# Else if our weather ID is directly equal to 771 one we will return.

# Let's return that.

# A violent gust of wind, a squall.

# 781 is for a tornado.

# Else if our weather ID is equal to 781, return a tornado.

# 800 exactly is for a clear sky.

# Else if our weather ID is equal to 800 return a sun, a sun emoji.

# Else if 801 is less than or equal to our weather ID which is less than or equal to 804.

# We will return some clouds.

# Now, if there are no matches, let's return an empty string to not display anything.

# Okay, let's do a test run.

# Let's look up Miami.


# We get scattered clouds.

# It's 94°.

# Los Angeles.

# Got the weather.

# We have a clear sky and a sun.

# Now, there's one fix we need to make.

# Let's say that I make up a city again.

# Blah blah blah.

# Got the weather.

# We should clear our emoji label and the weather description.

# But we still get that error message.

# So after we display our error within the display_error method,

# after we set the text of the temperature label,

# let's take the emoji label self.emoji_label and call the clear method to clear it.

self.emoji_label.clear()

# Then we have to do this with the description label self.description_label and call the clear method.

self.description_label.clear()

# Now we should be able to clear it when we get an error.

# Okay, let's look up Houston.

# Get the weather.

# Few clouds.

# 98°.

# Let's make up a city.


# Pizza City.

# Get the weather.

# Not found.

# City not found.

# And the emoji label and the weather description are cleared.

# Okay.

# What if I type in nothing?

# What happens?

# Let's get the weather.

# We have a bad request.

# Please check your input.

# That's if we have an HTTP status code of 400.

# We handled this exception.

# Bad request.

# All right, one last city.

# What about Paris?

# Let's get the weather.

# It is 68 degrees Fahrenheit and there's light rain.

# All right, everybody.

# So, that is a weather app that you can make using Python.

# Add it to your portfolio and thanks for watching.

```

You might also like