DEV Community

Cover image for Python from the word ...Go (Pt2)
Mark Gatere
Mark Gatere

Posted on

Python from the word ...Go (Pt2)

Basics Part2

Helloooo there! Welcome back!!
Wait, are you new here? Don't worry, I got you covered. Here, we are breaking the flow. Have you checked "Python from the word ...Go" Basics Part1? It's an awesome resource to first check out if you are not familiar with Python's variables and data types which comprise a few in-built Python data structures.
They are really gonna come in handy for this section.

Set? Lets' go!!


In the previous module, we learnt about the fundamental Python data types and also covered some of the terms used when talking about code like variables, statements, expressions, functions, methods ...etc.
Most importantly, we covered how to perform actions on the data types (Both functions and methods for each data type).
Up until now, we were still scratching the surface. Every time we write code, we wrote it line by line and hence our interpreter would go line by line running each code up to the last line.

>>> #do something
>>> #do something
>>> #do something
>>> #do something
Enter fullscreen mode Exit fullscreen mode

We are now going to incorporate the idea of running multiple lines over and over to discover the true power of programming for machines, haha!
Hence, in this section, we gonna talk about the idea of conditions and conditional logic. We gonna discuss more on looping and loops where we can perform actions multiple times over and over.
We are now going to break into a new world where instead of going from line by line in order, we gonna loop over till a condition is met.


  • Conditional Logic

We previously covered Boolean variables (True or False). When we come to conditional logic, Booleans are super important.
Example:
A person(a) wants to purchase a car from a company with specific conditions:

  1. The car must be new.
  2. The car must have a license.

Hence for person(a) to purchase the car:

  • is_new = True

  • is_licensed = True

In conditional logic, we use the 'if' keyword.
"If the car is new and licensed, then person(a) can purchase it".
Then, if any of the conditions in purchasing the car is not met, person(a) cannot purchase the car.

Example2:
Let's assume that for one to get into a specific event, the person has to be old(35 years and above). Create a program to only allow old people to the event.
If is_old = True, "allowed to get into the event."
For the syntax:

>>> is_old = True
>>> if is_old:
>>>   print("You are allowed to get in")
You are allowed to get in

>>> is_old = False
>>> if is_old:
>>>   print("You are allowed to get in")
#nothing will be printed out.
Enter fullscreen mode Exit fullscreen mode

Note: Any code that comes after the colon in the condition is automatically indented hence run if the condition is True whereas any code that ain't indented after the condition is not under the condition and hence run separately.
Example:

>>> is_old = True
>>> if is_old:
>>>   print("You are allowed to get in")
>>> print("Hello there")
You are allowed to get in
Hello there

>>> is_old = False
>>> if is_old:
>>>   print("You are allowed to get in")
>>> print("Hello there")
Hello there
Enter fullscreen mode Exit fullscreen mode

What if when a condition is not met(False), we want to perform another condition?

Here is an example:
I leave my house;
If it's cloudy, I bring an umbrella;
otherwise, I bring sunglasses.

  • We use the keyword 'else'.

Using our example of letting people in for the event, we can add:
If is_old = True, "allowed to get into the event.", otherwise if is_old = False, "not allowed to get in",

>>> is_old = True
>>> if is_old:
>>>   print("You are allowed to get in")
>>> else:
>>>   print("Not allowed in")
You are allowed to get in

>>> is_old = False
>>> if is_old:
>>>   print("You are allowed to get in")
>>> else:
>>>   print("Not allowed in")
Not allowed in
Enter fullscreen mode Exit fullscreen mode

What if by chance you have more than one condition?

Example:
I'm in a restaurant;
If I want meat, I order steak;
otherwise, If I want Pasta, I order spaghetti and meatballs;
otherwise, I order salad.

For such cases, we use the 'elif' keyword (else if).
Using a different example.

  • A person(b) wants to order chicken pizza. If there is no chicken pizza, the person(b) can order beef pizza; otherwise, if there is none, the person(b) can order rice.
>>> chicken_pizza = True
>>> beef_pizza = True

>>> if chicken_pizza:
>>>   print("Serve me chicken pizza.")
>>> elif beef_pizza:
>>>   print("Serve me beef pizza.")
>>> else:
>>>   print("Serve me rice.")
Serve me chicken pizza.

--------------------------------------------------
>>> chicken_pizza = False
>>> beef_pizza = True

>>> if chicken_pizza:
>>>   print("Serve me chicken pizza.")
>>> elif beef_pizza:
>>>   print("Serve me beef pizza.")
>>> else:
>>>   print("Serve me rice.")
Serve me beef pizza.

--------------------------------------------------
>>> chicken_pizza = False
>>> beef_pizza = False

>>> if chicken_pizza:
>>>   print("Serve me chicken pizza.")
>>> elif beef_pizza:
>>>   print("Serve me beef pizza.")
>>> else:
>>>   print("Serve me rice.")
Serve me rice.
Enter fullscreen mode Exit fullscreen mode

"For a person to be legalized to drive a car in public, one must have a national identification card and a driving license."
These are two conditions that one must have to avoid being fined by the cops.
To develop such a program, we must have both conditions True hence we can use the keyword 'and' to check whether both conditions are True.
When using 'and' both conditions must be True to execute the condition, if any of them is False, the program will run the 'elif' and 'else' part.

>>> has_id = True
>>> has_license = True

>>> if has_id and has_license:
>>>   print("Allowed to drive")
>>> else:
>>>   print("Not allowed to drive")
Allowed to drive

--------------------------------------------------
>>> has_id = False
>>> has_license = True

>>> if has_id and has_license:
>>>   print("Allowed to drive")
>>> else:
>>>   print("Not allowed to drive")
Not allowed to drive

--------------------------------------------------
>>> has_id = True
>>> has_license = False

>>> if has_id and has_license:
>>>   print("Allowed to drive")
>>> else:
>>>   print("Not allowed to drive")
Not allowed to drive
Enter fullscreen mode Exit fullscreen mode
  • Python Indentation

The interpreter in python finds meaning in the spacing hence indentation(tabs) and white spaces in python is essential.

  • Truthy Vs Falsy

In python, whenever there's a value, the interpreter recognizes that as True. Otherwise, when there's a zero(0) or no value, python interpreter recognizes that as False.
These are referred to as Truthy and Falsy values.

>>> print(bool('hello'))
>>> print(bool(5))
>>> print(bool(''))
>>> print(bool(0))
>>> print(bool(None))
True
True
False
False
False
Enter fullscreen mode Exit fullscreen mode

All values are considered "truthy" except the following; which are considered "falsy":

  • None

  • False

  • 0

  • 0.0

  • 0j

  • Decimal(0)

  • Fraction(0, 1)

  • [] - an empty list

  • {} - an empty dict

  • () - an empty tuple

  • '' - an empty str

  • b'' - an empty bytes

  • set() - an empty set

  • range(0) - an empty range

  • Objects for which:

    • obj.__bool__() returns False
    • obj.__len__() returns 0

Note: A "truthy" value will satisfy the check performed by if or while statements. We use "truthy" and "falsy" to differentiate from the bool values True and False.
Example:

>>> has_id = 'hello'
>>> has_license = 5

>>> if has_id and has_license:
>>>   print("Allowed to drive")
>>> else:
>>>   print("Not allowed to drive")
Allowed to drive

--------------------------------------------------
>>> has_id = 'hello'
>>> has_license = 0

>>> if has_id and has_license:
>>>   print("Allowed to drive")
>>> else:
>>>   print("Not allowed to drive")
Not allowed to drive

--------------------------------------------------
>>> has_id = None
>>> has_license = True

>>> if has_id and has_license:
>>>   print("Allowed to drive")
>>> else:
>>>   print("Not allowed to drive")
Not allowed to drive

--------------------------------------------------
>>> has_id = True
>>> has_license = ''

>>> if has_id and has_license:
>>>   print("Allowed to drive")
>>> else:
>>>   print("Not allowed to drive")
Not allowed to drive
Enter fullscreen mode Exit fullscreen mode

A good though not perfect example on the use of "truthy" and "falsy" application is in forms or in keying in log in credentials.

Image description

When a field is set to as 'a required field', the system expects the field to be "truthy" hence should not be left blank as this will lead to it being assigned "falsy".

  • Ternary Operator (Conditional Expression)

This is another way to do conditional logic. This works the same as 'if statements' but can in a way be referred to as a 'shortcut' so can only be used in certain conditional logic.
In this mode, we start with the condition then incorporate the "if statement".
"condition_if_true" if condition else "condition_if_false"

Example:
Let's use an example to determine if a user is your friend (eg. on Facebook whether the user can message you).

>>> is_friend = True
>>> can_message = "Message allowed" if is_friend else "not allowed to message"
>>> print(can_message)
Message allowed

>>> is_friend = True
>>> can_message = "Message allowed" if is_friend else "not allowed to message"
>>> print(can_message)
not allowed to message
Enter fullscreen mode Exit fullscreen mode
  • Short Circuiting

Previously we saw how to use the 'and' keyword to validate whether both statements are True:

>>> is_friend = True
>>> is_user = True
>>> print(is_friend and is_user)
True

>>> is_friend = True
>>> is_user = False
>>> print(is_friend and is_user)
False
Enter fullscreen mode Exit fullscreen mode

In short circuiting, the interpreter ignores one part of the condition(despite it being false) and returns either True or False.
Example, using the 'or' keyword, if the first part of the condition is True the interpreter returns True without checking the second part.
When we use the 'and' keyword, when the the first part of the statement is False, the interpreter ignores(short circuits) the second part and returns False.
An example using the 'or' keyword:

>>> is_friend = True
>>> is_user = False
>>> print(is_friend or is_user)
True

>>> is_friend = True
>>> is_user = True
>>> print(is_friend or is_user)
True

>>> is_friend = False
>>> is_user = True
>>> if is_friend or is_user:
>>>   print("Best friends forever")
Best friends forever

>>> is_friend = False
>>> is_user = True
>>> if False or is_user:
>>>   print("Best friends forever")
>>> else:
>>>   print("Never friends")
Best friends forever

>>> is_friend = True
>>> is_user = False
>>> if False or is_user:
>>>   print("Best friends forever")
>>> else:
>>>   print("Never friends")
Never friends

>>> if True or True:
>>>   print("Best friends forever")
>>> else:
>>>   print("Never friends")
Best friends forever
Enter fullscreen mode Exit fullscreen mode
  • Logical Operators

We have looked at a few logical operators previously 'and' and 'or'. A logical operator allows us to perform logic between two things.
Other logical operators include:

  • Greater than >

  • Less than <

  • Equal to ==

  • Greater than or equal to >=

  • Less than or equal to <=

  • Not equal to != (Opposite of equal to)

  • not keyword / Function - It negates the statement. Can also be written with bracket: not().

They return either True or False:

>>> print(4 > 5)
False

>>> print(4 < 5)
True

>>> print(4 == 5)
False

>>> print(1 >= 0)
True

>>> print(1 <= 0)
False

>>> print(0 >= 0)
True

>>> print(0 != 0)
False

>>> print(not(True))
False

>>> print(not True)
False

>>> print(not False)
True

>>> print(not(1 == 1))
False

>>> print(not(1 != 1))
True
Enter fullscreen mode Exit fullscreen mode

Note: A single equal sign = symbol is used in assigning values to variables hence to use the "equal to" operator for comparison, we use a double == symbol.

>>> print('a' > 'b')
False

>>> print('a' > 'A')
True
Enter fullscreen mode Exit fullscreen mode

But why/how is 'a' > 'b' False; and 'a' > 'A' True?

In the case of strings, Python compares the ASCII values of the characters. Hence, 'a' ASCII value is 97, 'b' is 98 and 'A' ASCII value is 65 that's why 'a' is greater than 'A' and 'b' is greater than 'a'.

*optional
In the case of, print('abc' < 'bac'), the result will be True. (Though this is a bit beyond the scope of the course).
This kind of comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted.

Lexicographical ordering for strings uses the Unicode code point number to order individual characters.

>>> print(1 < 2 < 3 < 4)
True

>>> print(1 < 2 > 3 < 4)
False
Enter fullscreen mode Exit fullscreen mode
  • Exercise1 (Done below)

You have been hired by a gaming company to create a program for a game where the character has magic powers and is an expert.
If the character has magic and is an expert, the output should be "You are a master magician". Otherwise, if the character has magic but is not an expert, the output should be "At least you're getting there". Else, if the character has no magic, the output should be "You need magic powers".

>>> print("Enter '1'(Yes) or '0'(No) for each question.")
>>> has_magic = bool(int(input("Does the character has magic? ")))
>>> is_expert = bool(int(input("Is the character an expert? ")))

>>> if has_magic and is_expert:
>>>   print("You are a master magician.")
>>> elif has_magic and not is_expert:
>>>   print("At least you're getting there.")
>>> elif not has_magic:
>>>  print("You need magic powers.")
Enter '1'(Yes) or '0'(No) for each question.
Does the character has magic? _1_
Is the character an expert? _1_
You are a master magician.

(Re-run the program)
Enter '1'(Yes) or '0'(No) for each question.
Does the character has magic? _0_
Is the character an expert? _1_
You need magic powers.

(Re-run the program)
Enter '1'(Yes) or '0'(No) for each question.
Does the character has magic? _1_
Is the character an expert? _0_
At least you're getting there.
Enter fullscreen mode Exit fullscreen mode
  • 'is' keyword

Unlike the double equal sign ==, which compares the equality in values, is is a keyword that checks if the location in memory where one value is stored is the same as the other's.
Example:

>>> print(True == 1)
>>> print('' == 1)
>>> print('1' == 1)
>>> print([] == 0)
>>> print(10 == 10.0)
>>> print([] == [])
>>> print([1, 2, 3] == [1, 2, 3])
True
False
False
False
True
True
True

>>> print(True is 1)
>>> print('' is 1)
>>> print('1' is 1)
>>> print([] is 0)
>>> print(10 is 10.0)
>>> print([] is [])
>>> print([1, 2, 3] is [1, 2, 3])
False
False
False
False
False
False
False

>>> print(True is True)
True

>>> print('1' is '1')
True

>>> print(10 is 10)
True
Enter fullscreen mode Exit fullscreen mode

Once a list is created, it is stored in different memory space hence print([] is []) or print([1, 2, 3] is [1, 2, 3]) will always evaluate to False.

All Data Structures in Python are stored in different memory locations.


  • For Loops

Loops are one of the most powerful features of a programming languages. The concept of looping allows us to run lines of code over and over till we accomplish a specific task.
In creating a for loop, we use the keyword 'for'.
Example: for i in 'name':
'i' in the loop is a variable for each element in the loop and can be any different name: for item in 'name':, for teddy in 'name': and is created for each item in 'name'(iterable).
An iterable is something that can be looped over.

>>> for item in 'name':
>>>    print(item)
n
a
m
e

>>> for item in [1, 2, 3, 4]:
>>>    print(item)
1
2
3
4

>>> name = 'Mark'
>>> for i in name:
>>>    print(i)
M
a
r
k

>>> for item in {1, 2, 3, 4}:
>>>    print(item)
1
2
3
4

>>> for item in (1, 2, 3, 4):
>>>    print(item)
1
2
3
4

>>> for item in (1, 2, 3, 4):
>>>    print(item)
>>>    print(item)
>>>    print(item)
>>> print("Hello")

1
1
1
2
2
2
3
3
3
4
4
4
Hello

>>> for item in (1, 2, 3, 4):
>>>    print(item)
>>>    print(item)
>>>    print(item)
>>> print(item)
1
1
1
2
2
2
3
3
3
4
4
4
4
Enter fullscreen mode Exit fullscreen mode
  • Nested for loops
>>> for item in (1, 2, 3, 4, 5):
>>>     for x in ['a', 'b', 'c']:
>>>         print(item, x)
1 a
1 b
1 c
2 a
2 b
2 c
3 a
3 b
3 c
4 a
4 b
4 c
5 a
5 b
5 c
Enter fullscreen mode Exit fullscreen mode
  • Iterables

An iterable is an object or a collection that can be iterated over (looped over).
An iterable can be a list, tuple, dictionary, set and string. This means that one can go one by one checking each item in the collection.

  • Iterating over a dictionary
>>> user = {
>>>     'name' : 'Mark',
>>>     'age' : 30,
>>>     'can_swim' : False
>>>   }

>>> for item in user:
>>>     print(item)
name
age
can_swim
Enter fullscreen mode Exit fullscreen mode

When we iterate over a dictionary we only get the keys but can use the dictionary methods to loop over the dictionary items which includes its values.

  • One is 'x.items()' where we get the key-value pairs in tuples form.
>>> user = {
>>>     'name' : 'Mark',
>>>     'age' : 30,
>>>     'can_swim' : False
>>>   }

>>> for item in user.items():
>>>     print(item)
('name', 'Mark')
('age', 30)
('can_swim', False)
Enter fullscreen mode Exit fullscreen mode
  • Second is 'x.values()' where we get only the values in the dictionary.
>>> user = {
>>>     'name' : 'Mark',
>>>     'age' : 30,
>>>     'can_swim' : False
>>>   }

>>> for item in user.values():
>>>     print(item)
Mark
30
False
Enter fullscreen mode Exit fullscreen mode
  • Third is 'x.keys()' where we get only the keys in the dictionary. Works the same as iterating the dictionary without including a method.
>>> user = {
>>>     'name' : 'Mark',
>>>     'age' : 30,
>>>     'can_swim' : False
>>>   }

>>> for item in user.keys():
>>>     print(item)
name
age
can_swim
Enter fullscreen mode Exit fullscreen mode

What if you want to print the items (key and values) in the dictionary separately? We can use tuple unpacking.

>>> user = {
>>>     'name' : 'Mark',
>>>     'age' : 30,
>>>     'can_swim' : False
>>>   }

>>> for item in user.items():
>>>     key, value = item
>>>     print(key, value)
name Mark
age 30
can_swim False

(second way of unpacking)

>>> user = {
>>>     'name' : 'Mark',
>>>     'age' : 30,
>>>     'can_swim' : False
>>>   }

>>> for key, value in user.items():
>>>     print(key, value)
name Mark
age 30
can_swim False

Enter fullscreen mode Exit fullscreen mode
  • Exercise2 (Done below)

Building a simple 'counter' to loop over a list and sum up the items in the list. The list is provided below.
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

>>> my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> sum = 0
>>> for i in my_list:
>>>     sum += i
>>> print (sum)
55
Enter fullscreen mode Exit fullscreen mode
  • range() in loops

It returns an object that produces a sequence of integers from the start (which is inclusive) to stop (exclusive).

>>> print(range(100))
range(0, 100)

>>> print(range(0, 100))
range(0, 100)
Enter fullscreen mode Exit fullscreen mode

We can iterate a range of numbers.

>>> for num in range(20)
>>>    print(num)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

>>> for i in range(2, 15)
>>>    print(i)
2
3
4
5
6
7
8
9
10
11
12
13
14

>>> for i in range(10):
>>>    print('my name is')
my name is
my name is
my name is
my name is
my name is
my name is
my name is
my name is
my name is
my name is
Enter fullscreen mode Exit fullscreen mode

When one 'does not want to use' a variable name in the loop, the person can use an underscore _:

>>> for _ in range(20)
>>>    print(_)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

>>> for _ in range(10):
>>>    print('my name is')
my name is
my name is
my name is
my name is
my name is
my name is
my name is
my name is
my name is
my name is
Enter fullscreen mode Exit fullscreen mode
  • (start: stop: stepover) in range()
>>> for _ in range(0, 10, 2)
>>>    print(_)
0
2
4
6
8

>>> for _ in range(0, 10, -1)
>>>    print(_)
#nothing will be printed out

>>> for _ in range(10, 0, -1)
>>>    print(_)
10
9
8
7
6
5
4
3
2
1

>>> for _ in range(10, 0, -2)
>>>    print(_)
10
8
6
4
2

>>> for _ in range(2)
>>>    print(list(range(10)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Enter fullscreen mode Exit fullscreen mode
  • enumerate()

It returns each item in the iterable with its index in tuple form.

>>> for i in enumerate('mark'):
>>>    print(i)
(0, 'm')
(1, 'a')
(2, 'r')
(3, 'k')

>>> for i, j in enumerate('mark'):
>>>    print(i, j)
0 m
1 a
2 r
3 k

>>> for i, char in enumerate(list(range(100))):
>>>     if char == 50:
>>>         print(f"The index of 50 is: {i}")
The index of 50 is: 50

>>> for i, j in enumerate('Mark'):
>>>     if j == 'r':
>>>         print(f"The index of r is: {i}")
The index of r is: 2
Enter fullscreen mode Exit fullscreen mode

  • While Loops

In While loop, a command is run when a specific condition is met till the condition becomes false after constant looping.
eg:

>>> i = 0
>>> while 0 < 50:
>>>     print(i)
0
0
0
0
0
#will run infinitely for 0 will always be less than 50.

>>> i = 0
>>> while i < 50:
>>>     i += 5
>>>     print(i)
5
10
15
20
25
30
35
40
45
50
Enter fullscreen mode Exit fullscreen mode
  • break command in while loop

When a break keyword is used in while loop, it breaks the loop after the first run.

>>> i = 0
>>> while 0 < 50:
>>>     print(i)
>>>     break
0
Enter fullscreen mode Exit fullscreen mode
  • using else in while loop
>>> i = 0
>>> while i < 50:
>>>     i += 5
>>>     print(i)
>>> else:
>>>     print("Done with work")
5
10
15
20
25
30
35
40
45
50
Done with work

>>> i = 0
>>> while i > 50:
>>>     i += 5
>>>     print(i)
>>> else:
>>>     print("Done with work")
Done with work
Enter fullscreen mode Exit fullscreen mode

The else block in while loop will only execute when there is no break statement in the while loop.

>>> i = 0
>>> while i < 50:
>>>     i += 5
>>>     print(i)
>>>     break
>>> else:
>>>     print("Done with work")
0
Enter fullscreen mode Exit fullscreen mode
  • How for loops and while loops relates
>>> my_list = [1, 2, 3]
>>> for item in my_list:
>>>     print(item)
1
2
3
------------------------------------
>>> my_list = [1, 2, 3]
>>> i = 0
>>> while i < len(my_list):
>>>     print(my_list[i])
>>>     i += 1 
1
2
3
Enter fullscreen mode Exit fullscreen mode

While loops are more flexible for we have a conditional statement but for loops are simpler. With the while loop, we have to remember to hope the loop in the course or use a break statement, to avoid getting an infinite loop.

>>> while True:
>>>     input("Say something: ")
>>>     break
Say something: _hi_

>>> while True:
>>>     response = input("Say something: ")
>>>     if (response == "bye"):
>>>         break
Say something: _hi_
Say something: _hi_
Say something: _bye_
Enter fullscreen mode Exit fullscreen mode
  • break, continue, pass

The break keyword breaks out of the loop. The continue keyword continues the loop till the condition is met without running the indented line(s) below it. The pass keyword is used to pass to the next line. It is mainly used as a placeholder.

>>> my_list = [1, 2, 3]
>>> for item in my_list:
>>>    continue
>>>    print(item)
#nothing will be printed

>>> i = 0
>>> while i < len(my_list):
>>>    i += 1
>>>    continue
>>>    print(my_list[i])
#nothing will be printed

>>> my_list = [1, 2, 3]
>>> for item in my_list:
>>>    pass

>>> i = 0
>>> while i < len(my_list):
>>>    print(my_list[i])
>>>    i += 1
>>>    pass
1
2
3
Enter fullscreen mode Exit fullscreen mode
  • Exercise3 (Done below)

Our First GUI Exercise 🥳 (Basic Version)

In this exercise, we are going to simulate what the computer does when we have a graphical user interface (GUI).

Assuming below are the pixels of an image (Christmas Tree in a nested list),;

picture = [
   [0, 0, 0, 1, 0, 0, 0],
   [0, 0, 1, 1, 1, 0, 0],
   [0, 1, 1, 1, 1, 1, 0],
   [1, 1, 1, 1, 1, 1, 1],
   [0, 0, 0, 1, 0, 0, 0],
   [0, 0, 0, 1, 0, 0, 0]
]
Enter fullscreen mode Exit fullscreen mode

You gonna loop over the list(s) and the moment you encounter a zero(0), you will display on the screen 'an empty space' but when you encounter a one(1), you gonna simulate a pixel hence display a star(*).

For this challenge, we will include a new special print parameter(option) known as 'end ='.
By default, the print function ends with a newline (when we print anything, we get a new line for the next statement). Passing the whitespace to the end parameter (end=' ') indicates that the end character has to be identified by whitespace and not a newline which we will use, for in this case, we don't want a new line after printing each and every character.
end = - "String appended after the last value".

(Without using the 'end' parameter)

>>> picture = [
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 1, 1, 0],
       [1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0]
    ]

>>> for row in picture:
>>>    for pixel in row:
>>>       if pixel == 0:
>>>          print(" ")
>>>       else:
>>>          print("*")



*





*
*
*



*
*
*
*
*

*
*
*
*
*
*
*



*






*




Enter fullscreen mode Exit fullscreen mode

(After using the end parameter to include a blank space 'empty string' after the end of the line)

>>> picture = [
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 1, 1, 0],
       [1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0]
    ]

>>> for row in picture:
>>>    for pixel in row:
>>>       if pixel == 0:
>>>          print(" ", end = ' ')
>>>       else:
>>>          print("*", end = ' ')

      *           * * *       * * * * *   * * * * * * *       *             *       
Enter fullscreen mode Exit fullscreen mode

(After each loop of the pixels, we also wanna include a blank space 'empty string' so that each single loop will be by default in each line):

>>> picture = [
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 1, 1, 0],
       [1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0]
    ]

>>> for row in picture:
>>>    for pixel in row:
>>>       if pixel == 0:
>>>          print(" ", end = ' ')
>>>       else:
>>>          print("*", end = ' ')
>>>    print(" ")

      *        
    * * *      
  * * * * *
* * * * * * *
      *
      *
Enter fullscreen mode Exit fullscreen mode
  • Exercise4 (Done below)

(Finding Duplicates in a list using loops and conditional logic)
Note: No use of sets in this case.
After finding the duplicate values, the program should print the duplicate values.

>>> some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
>>> duplicates = []
>>> for value in some_list:
>>>    if some_list.count(value) > 1:
>>>        if value not in duplicates:
>>>            duplicates.append(value)
>>> print(duplicates)
['b', 'n']
Enter fullscreen mode Exit fullscreen mode

  • Functions

Up until now, we've worked with python functions like print, list, input function and many more that allowed us to perform actions on our data types.
We can also create our own functions and use them on our program.
When creating functions in Python we use the def - 'define' keyword. We then give our function a name (defining the function) as we do with variables then we add the brackets () and a colon at the end.
For one to use a function one has to 'call it'.

>>> def say_hello(): #defining the function
>>>    print("Hellooo")
>>> say_hello() #calling the function
Hellooo
Enter fullscreen mode Exit fullscreen mode

Functions are super useful because the work under the principle of 'DRY - Don't Repeat Yourself' because instead of the programmer re-typing the code each and every time, the programmer can just call the function as many times as possible to run a specific block of code.

Example: Using our Christmas tree example above, we can use the function to output it multiple of times.

>>> picture = [
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 1, 1, 0],
       [1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0]
    ]

>>> def show_tree():
>>>    for row in picture:
>>>        for pixel in row:
>>>            if pixel == 0:
>>>                print(" ", end = ' ')
>>>            else:
>>>                print("*", end = ' ')
>>>        print(" ")

>>> show_tree()
>>> show_tree()
>>> show_tree()

      *        
    * * *      
  * * * * *
* * * * * * *
      *
      *
      *
    * * *
  * * * * *
* * * * * * *
      *
      *
      *
    * * *
  * * * * *
* * * * * * *
      *
      *
Enter fullscreen mode Exit fullscreen mode

The function is stored in a specific place in memory once created.

>>> def say_hello(): 
>>>    print("Hellooo")
>>> print(say_hello)
<function say_hello at 0x000002332BB33E20>

#The characters '0x000002332BB33E20' show the memory location where the function has been stored.
Enter fullscreen mode Exit fullscreen mode
  • Arguments Vs Parameters(in functions)

The power of functions beyond it being able to be called multiple times, is the ability of the programmer to make it dynamic. In its brackets, one can pass parameters.
The values which are defined at the time of the function prototype or definition of the function are called as parameters.
When a function is 'called', the actual values that are passed during the 'call' are called as arguments.

>>> def say_hello(name, age): #name and age are parameters.
>>>    print(f"Hello {name}, You're {age} yrs")
>>> say_hello("Mark", 20) #"Mark" and 20 are arguments.
Hello Mark, You're 20 yrs

>>> def say_hello(name, age):
>>>    print(f"Hello {name}, You're {age} yrs")
>>> say_hello("Mark", 20)
>>> say_hello("Emily", 19)
>>> say_hello("Dan", 17)
Hello Mark, You're 20 yrs
Hello Emily, You're 19 yrs
Hello Dan, You're 17 yrs
Enter fullscreen mode Exit fullscreen mode

The above arguments are referred to as positional arguments because they are required to be in the proper position.

>>> def say_hello(name, age):
>>>    print(f"Hello {name}, You're {age} yrs")
>>> say_hello(20, "Mark")
Hello 20, You're Mark yrs
Enter fullscreen mode Exit fullscreen mode
  • Default Parameters and Keyword Arguments

Keyword arguments, as opposed to positional arguments, allow us to not worry about the position hence the arguments can be in any position.
However this makes the code more complicated and not a proper practice way.

>>> def say_hello(name, age):
>>>    print(f"Hello {name}, You're {age} yrs")
>>> say_hello(age = 20, name = "Mark")
Hello Mark, You're 20 yrs
Enter fullscreen mode Exit fullscreen mode

Default parameters allow us to give constant values as we define the function. Default parameters only work when no values have been passed as arguments to the function.

>>> def say_hello(name = "Emily", age = 17):
>>>    print(f"Hello {name}, You're {age} yrs")
>>> say_hello()
Hello Emily, You're 17 yrs

>>> def say_hello(name = "Emily", age = 17):
>>>    print(f"Hello {name}, You're {age} yrs")
>>> say_hello("Dan", 23)
>>> say_hello()
Hello Dan, You're 23 yrs
Hello Emily, You're 17 yrs

>>> def say_hello(name = "Emily", age = 17):
>>>    print(f"Hello {name}, You're {age} yrs")
>>> say_hello("Irene")
Hello Irene, You're 17 yrs
Enter fullscreen mode Exit fullscreen mode
  • Return Statement

This is a keyword in python mostly used together with functions.
Functions always have to return something and when there is no return statement, the function will always return None.

>>> def sum(num1, num2):
>>>    num1 + num2
>>> print(sum(4, 5))
None
Enter fullscreen mode Exit fullscreen mode

When the return statement is used;

>>> def sum(num1, num2):
>>>    return num1 + num2
>>> print(sum(4, 5))
9
Enter fullscreen mode Exit fullscreen mode

A function should do one thing really well and/or should return something. This however doesn't mean that the code only has to be one line.

>>> def sum(num1, num2):
>>>    return num1 + num2
>>> total = sum(10, 5)
>>> print(sum(10, total))
25

>>> def sum(num1, num2):
>>>    return num1 + num2
>>> print(sum(10, sum(10, 5)))
25

>>> def sum(num1, num2):
>>>    def another_func(num1, num2):
>>>       return num1 + num2
>>> total = sum(10, 20)
>>> print(total)
None

def sum(num1, num2):
   def another_func(num1, num2):
      return num1 + num2
   return another_func
total = sum(10, 20)
print(total)
<function sum.<locals>.another_func at 0x000002387BF49B40>

>>> def sum(num1, num2):
>>>    def another_func(num1, num2):
>>>       return num1 + num2
>>>    return another_func
>>> total = sum(10, 20)
>>> print(total(10, 20))
30

>>> def sum(num1, num2):
>>>    def another_func(num1, num2):
>>>       return num1 + num2
>>>    return another_func(num1, num2)
>>> total = sum(10, 20)
>>> print(total)
30
Enter fullscreen mode Exit fullscreen mode

To avoid confusion when working with more than one function (function in a function), it is advisable to use different names for the parameter.

>>> def sum(num1, num2):
>>>    def another_func(n1, n2):
>>>       return num1 + num2
>>>    return another_func(num1, num2)
>>> total = sum(10, 20)
>>> print(total)
30
Enter fullscreen mode Exit fullscreen mode

Note: A return keyword automatically exits the function in that any code (to output) below the return statement is never run.

>>> def sum(num1, num2):
>>>    def another_func(n1, n2):
>>>       return num1 + num2
>>>    return another_func(num1, num2)
>>>    return 5
>>>    print("Hello")
>>> total = sum(10, 20)
>>> print(total)
30
Enter fullscreen mode Exit fullscreen mode
  • Methods Vs Functions

Examples of inbuilt functions in python include : list(), print(), max(), min(), input(). We've also found out that we can use the keyword def to define our own functions.

When using methods, we use the dot(.) notation. Methods are owned by 'whatever is to the left of the dot(.)' be it strings, tuples, integers etc. Methods of the fundamental data types have been covered in the previous module where all the data types have been covered too.
Both Functions and Methods allow us to take actions on the data types.
None: Similar to functions, we can also build our own methods which will be explained in details in the next module as we discuss on 'classes and objects'.

  • Docstrings

This is using triple quotes ('''...''') to 'comment' multiple lines. It can also be used in functions to give more info about a function.

Image description

It works the same as the more info about a function provided by the developer environments when typing the inbuilt functions.

Image description

  • Help function

Used to give more info about a function. When a docstring is passed in a function, the help function returns the docstring.

>>> help(print)

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.

-----------------------------------------------------------------

>>> def test(a):
       '''
       Info: This is a function that prints the testing data.
       '''
       print(a)
>>> help(test)
test(a)
    Info: This is a function that prints the testing data.
Enter fullscreen mode Exit fullscreen mode

We can also use the dunder method (Magic method) 'will get into it later in the course' to get more info on a function.

>>> def test(a):
       '''
       Info: This is a function that prints the testing data.
       '''
       print(a)
>>>print(test.__doc___)
Info: This is a function that prints the testing data.
Enter fullscreen mode Exit fullscreen mode

Docstrings are really useful to add comments and definition to a function to enable other people understand what your function does without searching through your multiple files.

  • Writing clean code
>>> def is_even(num):
>>>    if num % 2 == 0:
>>>      return True
>>>    elif num % 2 != 0:
>>>      return False
>>> print(is_even(50))
True
Enter fullscreen mode Exit fullscreen mode

Vs

>>> def is_even(num):
>>>    if num % 2 == 0:
>>>      return True
>>>    else:
>>>      return False
>>> print(is_even(50))
True
Enter fullscreen mode Exit fullscreen mode

Vs

>>> def is_even(num):
>>>    if num % 2 == 0:
>>>      return True
>>>    return False
>>> print(is_even(50))
True
Enter fullscreen mode Exit fullscreen mode

Vs

>>> def is_even(num):
>>>    return num % 2 == 0
>>> print(is_even(50))
True
Enter fullscreen mode Exit fullscreen mode
  • *args and **kwargs

*args - arguments
**kwargs - Keyword arguments

In function we have special characters called *args and **kwargs.

>>> def super_func(num):
>>>    return sum(num)
>>> super_func(1, 2, 3, 4, 5)
#returns an error because the function should take just 1 positional argument but we gave 5.
Enter fullscreen mode Exit fullscreen mode

_args (adding an asterisk at the start of the parameter) allows one to pass more than one positional argument.

>>> def super_func(*num):
>>>    print(*num)
>>>    return sum(num)
>>> super_func(1, 2, 3, 4, 5)
1 2 3 4 5

>>> def super_func(*num):
>>>    print(num)
>>>    return sum(num)
>>> super_func(1, 2, 3, 4, 5)
(1 2 3 4 5)

>>> def super_func(*num):
>>>    return sum(num)
>>> print(super_func(1, 2, 3, 4, 5))
15
Enter fullscreen mode Exit fullscreen mode

__kwargs allows us to use keyword arguments. It returns a dictionary of the values.

>>> def super_func(*num, **kwargs):
>>>    print(kwargs)
>>> super_func(1, 2, 3, 4, 5, num1=10, num2=15)
{'num1': 10, 'num2': 15}

>>> def super_func(*num, **kwargs):
>>>    print(kwargs)
>>>    total = 0
>>>     for items in kwargs.values():
>>>        total += items
>>>     return sum(num) + total
>>> print(super_func(1, 2, 3, 4, 5, num1=10, num2=15))
{'num1': 10, 'num2': 15}
40
Enter fullscreen mode Exit fullscreen mode

There's a rule of positioning when one is using parameters, default parameters, *args and **kwargs:

parameters, *args, default parameters, **kwargs

>>> def super_func(name, *num, greet="hi", **kwargs):
>>>    print(kwargs)
>>>    total = 0
>>>     for items in kwargs.values():
>>>        total += items
>>>     return sum(num) + total
>>> print(super_func("Andy", 1, 2, 3, 4, 5, num1=10, num2=15))
{'num1': 10, 'num2': 15}
40
Enter fullscreen mode Exit fullscreen mode
  • Exercise5 (Done below)

Create a function called 'highest_even' that is passed a list of integers and returns the highest even integer.
The list is given below:
my_list = [10, 2, 3, 8, 4, 11]

>>> my_list = [10, 2, 3, 4, 8, 11]
>>> even_list = []
>>> def highest_even():
>>>    for i in my_list:
>>>       if i % 2 == 0:
>>>          even_list.append(i)
>>>          even_list.sort()
>>>          print(even_list)
>>>    print(even_list[-1])
>>> highest_even()
[10]
[2, 10]
[2, 4, 10]
[2, 4, 8, 10]
10

-------------------------------------------------------------

>>> my_list = [10, 2, 3, 4, 8, 11]
>>> even_list = []
>>> def highest_even():
>>>    for i in my_list:
>>>       if i % 2 == 0:
>>>          even_list.append(i)
>>>          even_list.sort()
>>>    print(even_list[-1])
>>> highest_even()
10
Enter fullscreen mode Exit fullscreen mode

We can also use the max keyword to return the maximum even number:

>>> my_list = [10, 2, 3, 4, 8, 11]
>>> def highest_even():
>>>    even_list = []
>>>    for i in my_list:
>>>       if i % 2 == 0:
>>>          even_list.append(i)
>>>    return max(even_list)
>>> print(highest_even())
10
Enter fullscreen mode Exit fullscreen mode
  • Walrus Operator (:=)

The walrus operator is a new feature in python 3.8.
It is explained in depths in the python documentation.
It is used to assign values to variables as part of a 'larger expression' eg. in if statements, in while loops etc.

>>> a = "Helloooooooooooooooo"
>>> if (len(a) > 10):
>>>     print(f"Too long, {len(a)} elements")
Too long, 20 elements
Enter fullscreen mode Exit fullscreen mode

Using the walrus operator:

>>> a = "Helloooooooooooooooo"
>>> if ((n := len(a)) > 10):
>>>     print(f"Too long, {n} elements")
Too long, 20 elements
Enter fullscreen mode Exit fullscreen mode

Walrus Operator (while loop)

>>> a = "Helloooooooooooooooo"
>>> while ((n := len(a)) > 1):
>>>     print(n)
>>>     a = a[:-1]
>>> print(a)
20
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
H

-----------------------------------------------------------------

>>> a = "Helloooooooooooooooo"
>>> while ((n := len(a)) > 1):
>>>     print(a)
>>>     a = a[:-1]
>>> print(a)
Helloooooooooooooooo
Hellooooooooooooooo
Helloooooooooooooo
Hellooooooooooooo
Helloooooooooooo
Hellooooooooooo
Helloooooooooo
Hellooooooooo
Helloooooooo
Hellooooooo
Helloooooo
Hellooooo
Helloooo
Hellooo
Helloo
Hello
Hell
Hel
He
H
Enter fullscreen mode Exit fullscreen mode

  • Scope

"What variables do I have access to?"

>>> print(name)
#returns an error; NameError: name 'name' is not defined
Enter fullscreen mode Exit fullscreen mode

Once a variable is not defined, one cannot have access to it.
A variable with a global scope is a variable that can be accessed by anybody in the file and can be used anywhere; in the conditional logic; in the while loop; in a function, etc.

>>> total = 100
>>> print(total)
100

#total has a global scope
Enter fullscreen mode Exit fullscreen mode

A variable with a functional scope is a variable that can only be accessed in within the function.

>>> def some_func():
>>>    total = 100
>>> print(total)
#returns an error; NameError: name 'total' is not defined

>>> def some_func():
>>>    total = 100
>>>    print(total)
100
Enter fullscreen mode Exit fullscreen mode
  • Scope Rules

"What would you expect the output of the code below to be?"

>>> a = 1
>>> def confusion():
>>>   a = 5
>>>   return a
>>> print(a)
>>> print(confusion())
Enter fullscreen mode Exit fullscreen mode

The output will be 1 and 5. This is because the first print function, print(a), outputs the value stored in the a with the global scope while the second print function, print(confusion()), returns the value stored in the a variable in the function.
The a is not modified after the function because of scope.

Rules the interpreter follow in scope:

1. Local Scope - Scope within the functional scope.
>>> a = 1
>>> def confusion():
>>>   a = 5
>>>   return a
>>> print(confusion())
>>> print(a)
5
1

>>> a = 1
>>> def confusion():
>>>   return a
>>> print(confusion())
>>> print(a)
1
1
Enter fullscreen mode Exit fullscreen mode
2. Parent Local scope - Works where there's a function within a function.
>>> a = 1
>>> def parent():
>>>   a = 10
>>>   def confusion():
>>>      return a
>>>   return confusion()
>>> print(parent())
>>> print(a)
10
1
Enter fullscreen mode Exit fullscreen mode
3. Global scope
>>> a = 1
>>> def parent():
>>>   def confusion():
>>>      return a
>>>   return confusion()
>>> print(parent())
>>> print(a)
1
1
Enter fullscreen mode Exit fullscreen mode
4. Built in python functions
>>> a = 1
>>> def parent():
>>>   def confusion():
>>>      return sum
>>>   return confusion()
>>> print(parent())
>>> print(a)
<built-in function sum>
1
Enter fullscreen mode Exit fullscreen mode

Note: Parameters are part of the local scope:

>>> b = 10
>>> def confusion(b):
>>>    print(b)
>>> confusion(300)
300

>>> b = 10
>>> def confusion(b):
>>>    print(b)
>>> confusion(b)
10
Enter fullscreen mode Exit fullscreen mode
  • Global keyword

"What if one wants to refer to a global variable while in the function without creating a new variable?"
Example:

>>> total = 0
>>> def count():
>>>    total += 1
>>>    return total
>>> print(count())
#returns an error; UnboundLocalError: local variable 'total' referenced before assignment
Enter fullscreen mode Exit fullscreen mode

The error occurs because the function count does not recognize total. Hence, we have to add a variable total in the function:

>>> total = 0
>>> def count():
>>>    total = 0
>>>    total += 1
>>>    return total
>>> print(count())
1
Enter fullscreen mode Exit fullscreen mode

To avoid recreating another variable in the function we can use the global keyword to tell the interpreter that we want to use the global scope on the variable in the function.

>>> total = 0
>>> def count():
>>>    global total
>>>    total += 1
>>>    return total
>>> print(count())
1

>>> total = 0
>>> def count():
>>>    global total
>>>    total += 1
>>>    return total
>>> count()
>>> count()
>>> print(count())
3
Enter fullscreen mode Exit fullscreen mode
  • Dependency injection (A simplified version of global scope)
>>> total = 0
>>> def count(total):
>>>    total += 1
>>>    return total
>>> print(count(total))
1

>>> total = 0
>>> def count(total):
>>>    total += 1
>>>    return total
>>> print(count(count(count(total))))
3
Enter fullscreen mode Exit fullscreen mode
  • Nonlocal Keyword

This is a new keyword(feature) in python 3 and its used to refer to the parent local scope.

>>> def outer():
>>>    x = "local"
>>>    def inner():
>>>       nonlocal x
>>>       x = "nonlocal"
>>>       print("inner:", x)
>>>    inner()
>>>    print("outer:", x)
>>> outer()
inner: nonlocal
outer: nonlocal

#Without the nonlocal keyword

>>> def outer():
>>>    x = "local"
>>>    def inner():
>>>       x = "nonlocal"
>>>       print("inner:", x)
>>>    inner()
>>>    print("outer:", x)
>>> outer()
inner: nonlocal
outer: local
Enter fullscreen mode Exit fullscreen mode

Why do we need scope?

"Why not just have every variable with a global scope so that everything has access to everything?"
Machines don't have infinite power/memory hence we need to be cautious of the resources we use. Scope is a good demonstration of this.
Eg. When a function is run, we create one memory space hence when for instance we use nonlocal, we instead of creating another memory space, we use an existing one.


Hurraay!! 🥳
We come to the end of the second module. Hope you've learnt a lot and ready to implement the skills in different fields.
As we wrap up, here is a simple exercise for you to try before checking the answer done below:

>>> age = input("What is your age?: ")
>>> if int(age) < 18:
>>>     print("Sorry, you are too young to drive this car. Powering off!")
>>> elif int(age) > 18:
>>>     print("Powering On. Enjoy the ride!");
>>> else:
>>>     print("Congratulations on your first year of driving. Enjoy the ride!")
Enter fullscreen mode Exit fullscreen mode

Given the code above, perform the actions below on the code:

  1. Wrap the above code in a function called checkDriverAge() that whenever you call this function, you will get prompted for age.

  2. Instead of using the input(), make the checkDriverAge() function accept an argument of age, so that if you enter eg. checkDriverAge(92), it returns "Powering On. Enjoy the ride!"

  3. Make the default age set to 0 if no argument is given.

Answer:

>>> def checkDriverAge(age=0):
>>>    if int(age) < 18:
>>>        print("Sorry, you are too young to drive this car. Powering off")
>>>    elif int(age) > 18:
>>>        print("Powering On. Enjoy the ride!");
>>>    else:
>>>        print("Congratulations on your first year of driving. Enjoy the ride!")
>>> checkDriverAge(10)
Sorry, you are too young to drive this car. Powering off
Enter fullscreen mode Exit fullscreen mode

What next? 🤔
Let's meet in the next module #Advanced Python1.

Till next time; bye bye.

Discussion (2)

Collapse
itsfoss profile image
ItsFoss

Nice Article Mark.
This really helped me jog my python memory. Some pretty concrete examples you have highlighted in here. Cant wait for part three of the same :)

Collapse
gateremark profile image
Mark Gatere Author

Awesome, happy learning ItsFoss