3. Control and Functions
3.1. Conditionals and Booleans
if
statements are used to control the flow of the program. It allows us to execute a block of code if a certain condition is met.
else
statements are used to execute a block of code if the condition is not met.
elif
is to add more conditions to the if
statement, which stands for else if
.
3.1.1. if
and Boolean Values
What if we change the condition to False
?
False
, so the block of code is not executed.
In real practice, we don't hardcode the condition to be True
or False
, we basically assess the condition to be True
or False
For example, we can use comparison operators to compare two values:
Comparison Operators
Recall the operator we used in the previous chapter. This time, we use them in the condition, plus the identity operator is
.
- Equal:
==
- Not Equal:
!=
- Greater Than:
>
- Less Than:
<
- Greater or Equal:
>=
- Less or Equal:
<=
- Identity:
is
3.1.2. if
, else
and elif
Let's continue on the previous example. We campare pet
and Tub
to see if the pet is Tub
If we change the value of pet
to Barkalot
, the condition is not met, so the else
statement is executed.
We can also use elif
to add more conditions to the if
statement. Here we have two conditions, pet == 'Tub'
and pet == 'Barkalot'
. If the first condition is not met, we move on to the next condition. If the second condition is not met, we move on to the else
statement:
pet = 'Barkalot'
if pet == 'Tub':
print("Pet is Tub!")
elif pet == 'Barkalot':
print("Pet is Barkalot!")
else:
print("Pet is not Tub!")
3.1.3. is
vs. ==
Now, we investigate the difference between is
and ==
:
-
is
checks if two variables point to the same object in memory. -
==
checks if the values of two variables are equal.
Here, we have two list with the same values.
We use==
to compare them, and the result is True
.
This is because that the values of the two lists are the same.
If we use is
to compare them, the result is False
.
pet_1
and pet_2
point to different objects in memory.
We can check out the memory address of the two objects using id()
:
But if we assign pet_2
to pet_1
, they will point to the same object in memory, and of course have the same values.
3.1.4. and
, or
and not
We can use and
and or
to combine conditions.
-
and
means both conditions must be met -
or
means at least one condition must be met
For example, we want to check if both the account name is Tub
and the passcode is correct by using and
:
account_name = 'Tub'
account_passcode = True
if account_name == 'Tub' and account_passcode:
print("Login successful!")
else:
print("Login failed!")
If we want to know if at least one of the account name or account passcode is correct, we use or
:
account_name = 'Tub'
account_passcode = True
if account_name == 'Tub' or account_passcode:
print("Name or passcode is correct!")
else:
print("Name and passcode are incorrect!")
If we want to negate a condition, we use not
.
not
means the condition must not be metInputHere, we useaccount_passcode = True if not account_passcode: print("Please enter your passcode!") else: print("Login successful!")
not
to negate the conditionaccount_passcode == True
toaccount_passcode == False
. Therefore, the condition must not be met, and theelse
statement is not executed.
If we remove not
, the condition must be met, which is account_passcode == True
, and the if
statement is executed.
account_passcode = True
if account_passcode:
print("Please enter your passcode!")
else:
print("Login successful!")
3.1.5. in
and not in
We can use in
to check if a value is in a list.
-
in
means the value must be in the list -
not in
means the value must not be in the list
Here is the example of in
:
pets = ['Tub', 'Barkalot', 'Furrytail']
if 'Tub' in pets:
print("Tub is in the list!")
else:
print("Tub is not in the list!")
If we use not in
, the condition is negated, and the else
statement is executed:
pets = ['Tub', 'Barkalot', 'Furrytail']
if 'Tub' not in pets:
print("Tub is not in the list!")
else:
print("Tub is in the list!")
3.1.6. False Values
False Values
In Python, the following values are considered as False
:
False
None
0
(any zero numeric types)- Empty sequence. e.g.,
''
,()
,[]
. - Empty mapping. e.g.,
{}
.
False
is considered as False:
account_name = False
if account_name:
print("Login successful!")
else:
print("Please enter your account name!")
None
is considered as False:
account_name = None
if account_name:
print("Login successful!")
else:
print("Please enter your account name!")
Only number 0
is considered as False:
account_name = 0
if account_name:
print("Login successful!")
else:
print("Please enter your account name!")
Empty sequences, e.g. ''
, ()
, []
, are considered as False:
account_name = ''
if account_name:
print("Login successful!")
else:
print("Please enter your account name!")
Empty dictionary is considered as False
account_name = {}
if account_name:
print("Login successful!")
else:
print("Please enter your account name!")
3.2. Functions
In this section, we will walk you through various examples related to functions in Python, exploring different concepts such as defining and calling functions, arguments, default values, and more.
3.2.1. Functions Basics
First, let's define a simple function called hello_tub:
This function does nothing, as it contains a pass statement. When you print the function, you will get the memory address of the function object: Calling the function withhello_tub()
returns None, as the function has no return statement.
Now, let's modify the hello_tub function to print a greeting:
Using functions is advantageous when you want to reuse code. For instance, if you want to change the greeting from Tub
to Barkalot
, you only need to modify the function's implementation, and all the calls to the function will use the updated greeting.
For example, if we want to call Hello Tub
twice, we can do the following:
Tub
:
Functions can also return values, take parameters, and have default values for parameters. Here are some examples:
We can also call the function with a method, e.g. lower()
, to convert the returned value to lowercase:
Functions can also return values, take parameters, and have default values for parameters. Here are some examples:
We set up a default value for the name parameter, so that if we don't pass a value for name, the function will use the default value:
def hello_tub(greeting, name = 'Tub'):
return '{}, {}'.format(greeting, name)
print(hello_tub('Hello'))
When we pass a value for name, the default value is ignored:
3.2.2. Positional Arguments
In Python, non-default arguments (those without default values) must be defined before default arguments (those with default values). In the given code, the greeting parameter has a default value, while name does not. This causes a SyntaxError.
To fix this issue, you should move the non-default argument before the default argument:
Now, the function works as expected, and you can call it with or without providing a greeting argument:Below let's go through a real example how to find the number of days in a month
Example - Find the number of days in a month
Credits: Python Standard Library, and Corey Schafer.
Number of days per month. First value placeholder for indexing purposes.
Theis_leap()
function takes a year as input and returns True if it's a leap year, and False otherwise. Leap years are those divisible by 4, but not divisible by 100, unless they are also divisible by 400.
def is_leap(year):
"""
Return True for leap years, False for non-leap years.
"""
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
days_in_month()
function takes a year and a month as input and returns the number of days in that month for that year. It checks if the input year is a leap year and adjusts the number of days in February accordingly. If an invalid month is provided, it returns Invalid Month
.
def days_in_month(year, month):
"""
Return number of days in that month in that year.
"""
if not 1 <= month <= 12:
return 'Invalid Month'
if month == 2 and is_leap(year):
return 29
return month_days[month]
is_leap()
and days_in_month()
functions with specific inputs:
3.2.3. *args
and **kwargs
In the following code, there are several concepts being illustrated: *args
, **kwargs
, and two custom functions is_leap()
and days_in_month()
.
*args
and **kwargs
are used in function definitions to allow passing a variable number of arguments. *args
is used for passing a variable number of non-keyword (positional) arguments, while **kwargs
is used for passing a variable number of keyword arguments.
def pet_info(*args, **kwargs):
print(args)
print(kwargs)
pet_info('Tub', 'Barkalot', 'Furrytail', pet1 = 'Tub', pet2 = 'Barkalot', pet3 = 'Furrytail')
pet_info()
function, both *args
and **kwargs
are used. When you call the function with different types of arguments, you can see how they are grouped and printed:
def pet_info(*args, **kwargs):
print(args)
print(kwargs)
favorite_food = ['Carrot', 'Brocolli', 'Ice Cream']
info = {'name': 'Tub', 'age': 25}
pet_info(favorite_food, info)
favorite_food
and a dictionary info as arguments without using the *
or **
unpacking operators. This means the entire list and dictionary are treated as single positional arguments. The output shows that args contains a tuple with two elements: the list favorite_food
and the dictionary info. Since we didn't provide any keyword arguments, kwargs is an empty dictionary.
In the second call to pet_info, we use the *
and **
unpacking operators to pass the list favorite_food and the dictionary info as individual elements. The *
operator unpacks the list elements as positional arguments, and the **
operator unpacks the dictionary items as keyword arguments. In this case, the output shows that args contains a tuple with three elements ('Carrot
', 'Brocolli
', 'Ice Cream
') and kwargs contains a dictionary with the keys and values from the info dictionary.
3.2.3. Variable Scope - LEGB rule
In this section, we are discussing variable scope in Python, which determines where a variable can be accessed or modified. Python searches for a variable following the LEBG rule and order: Local, Enclosing, Global, and Built-in.
Local A variable defined within a function has local scope. It can only be accessed inside that function.
Global
A variable defined outside any function has global scope. It can be accessed both inside and outside of functions.
In the following example, we define a variable x
outside of the test()
function. We can access and modify this variable inside the function.
x
inside the function. However, if we try to access the local variable y
outside of the function, we get an error.
What if we have a variable with the same name inside and outside of a function? In this case, the local variable takes precedence over the global variable. The following example demonstrates this:
This example shows that the python searches for a variable in the local scope first. If it doesn't find it, it searches the global scope. This is the reason that we get the local variablex
inside the function first and the global variable x
next
If it doesn't find it there, it will throw an error.
In the next example, we demonstrate how to use the global keyword to change the value of a global variable within a function.
# What if we want to set a new global x
x = 'global variable x'
def test():
global x
x = 'local variable x'
print(x)
test()
print(x)
In this example, we use the global
keyword to change the value of the global variable x
inside the function, although this is not recommended in the practice because it can lead to unexpected behavior and make the code difficult to debug and review.
You can also do the following:
However, it is not recommended to use global often.
Built-in
In this example, we are discussing the built-in scope in Python. Built-in scope refers to the predefined functions and variables available in Python, which are part of the standard library.
Python has a set of built-in functions, like min(), max(), print(), etc., which are readily available for use.
However, you should avoid overwriting built-in functions with your own functions or variables. Doing so can lead to errors or unintended behavior.
To avoid conflicts with built-in functions, it's a good practice to use different names for your own functions.
So the best way to do this is to use a different name instead of the default name min()
.
Being mindful of built-in functions and avoiding name conflicts will help you write clean, error-free code.
Enclosing
In this example, we discuss the concept of enclosing scope in Python. Enclosing scope is the scope of variables that are defined in an outer function but not in the global scope. Enclosing scope variables are accessible from the inner function.
Let's look at an example:
x = 'global variable x'
def outer():
x = 'local-outer variable x'
def inner():
x = 'local-inner variable x'
print(x) # 1st print
inner() # 1st call for 1st print
print(x) # 2nd print
outer() # 2nd call for 1st print and 2nd print
print(x) # 3rd print
The outer()
function has its own local variable x
, and the inner()
function also has its own local variable x. When we call the functions, the inner function prints its local variable x
, the outer function prints its local variable x
, and then the global variable x is printed.
Now let's use the nonlocal keyword to modify the enclosing variable from the inner function:
x = 'global variable x'
def outer():
x = 'local-outer variable x'
def inner():
nonlocal x # Make our local-inner variable x to be the enclosing variable x
x = 'local-inner variable x'
print(x)
inner()
print(x)
outer()
print(x)
In this case, we use the nonlocal keyword inside the inner()
function to indicate that we want to modify the enclosing variable x
(the one defined in the outer()
function) instead of creating a new local variable. When the functions are called, both the inner and outer functions print the modified enclosing variable x
, and then the global variable x
is printed.