Session 3 : Python basics recap, loops, functions

Outline of this session:

  • 5min Check-in & starting the participation thread

  • 5min Notes on grading

  • 15min Recap of last session

  • 15min Loops

  • 30min Functions

  • 20min Open experimentation

There are three recordings in which I run through the following code snippets:

Loops

# In the last session we ended with for-loops that could loop
# through a list or a function. But what if we do not want to
# loop over some thing in particular, but just want to loop,
# maybe with a specific stop condition of our own.

# Well, let's do a simple while loop
# and we will use this number as a loop condition
number = 1
# no execute the loop as long as the number is smaller than 100
while number < 100:
    print(f'counting from {number} to {number+1}')
    # and maybe doing some more serious stuff here
    # but don't forget to update the loop condition, unless you want too loop forever
    number += 1

# Thanks to Al Sweigart for the following one
name = ''
while name != 'your name':
    print('Please type your name.')
    name = input()
print('Thank you!')


# now we add some more loop control structures with break and continue
boredom_threshold = 42  
number = 0  
while number < 100:  
    # increase the loop counter  
    number += 1  
    # we only want to deal with even numbers, so we just skip odd ones  
    if number % 2 != 0:  
        continue  
    # and in case we get past our boredom threshold, we just end the loop  
    if number > boredom_threshold:  
        break  
    print(f'we have reached {number}')  
    # and maybe do some more important stuff here as well

As a file: snippets/loops.py

Functions

'''
Functions
=========
... are blocks of reusable code, that can be run by invoking/calling the function
wherever we want to use that code. Functions can have zero or more arguments,
through which we can parameterise the function to not always do exactly the
same thing. Functions can also return some value, that we can use when the
function code has finished to run.

But let's start with some examples:'''

# we already used functions, e.g. the print function
print()  # this prints and empty line
print()  # again, does exactly the same thing
print('hey there!')  # now we use one argument (a string), so the function prints something else
print('again, but with some other argument')

# now print can take many arguments
print('it will print all those', 3, 'arguments, separated by a space')
# print even allows for some keyword arguments. check it out: https://devdocs.io/python~3.8/library/functions#print
print('a', 'set', 'of', 'random', 'data', 23, 43, sep=';', end=';# i know, csv does not do comments, but we add this at the end of the line anyways\n')

# or here a function we already used that returns something
the_return_value = input('Hey, gimme something: ')


'''
Defining our own functions
==========================
We are not limited to using the functions that are already there.
We can define our own functions. How awesome is that? This way we
could rewrite the whole language basically. But let's start with
some simple examples.
'''

# a function that just prints some lines of code
def print_greeting():
    print('Hello user!')
    print('What a lovely day.')
    print('Have fun hacking around.')
    
# now every time we want to print those three lines we don't have to write
# them all along but just do:
print_greeting()
print_greeting()  # see, just does the same thing again

# let's overwrite the function and make it a bit more versatile
# by introducing an argument
def print_greeting(name):
    print('Hello', name, end='!\n')
    print('What a lovely day.')
    print('Have fun hacking around.')

# now we can use the same function for different users
print_greeting('Ada')
print_greeting('Grace')
print_greeting('Hedy')

# let's add some more arguments with defaults
def print_greeting(name, daytime='day', activity='hacking around'):
    print('Hello', name, end='!\n')
    print(f'What a lovely {daytime}.')
    print(f'Have fun {activity}.')

# now we can use the same function for more personalised greetings
print_greeting('Ada')
print_greeting('Grace', 'morning')
print_greeting('Hedy', 'evening', 'frequency hopping')
print_greeting('Margaret', activity='launching space ships with code')


# now some other functions that actually return something
def my_own_much_better_addition(num1, num2):
    result = num1 + num2  # actually not really better, just an addition
    return result

# we now can call the function and it will provide some result
my_calc = my_own_much_better_addition(23, 42)
print(my_calc)

# something more interesting that returns a more interesting data structure too
def get_user_details():
    name = input('Hey user, what\'s your name? ')
    activity = input('And what is your favourite activity? ')
    return {
        "name": name,
        "activity": activity,
    }

# which we now can use every time a user logs on, to later print the greeting
user_details = get_user_details()
print_greeting(user_details['name'], activity=user_details['activity'])


# And now for an important but not that often used concept:
'''*****************************
   ***                       ***
   ***   R E C U R S I O N   ***
   ***                       ***
   *****************************'''
# do you know the factorials?
# mathematically written: n! = n * (n-1)!
# more background: https://en.wikipedia.org/wiki/Factorial
# here's a list of the first few factorials there are (for 0 and 1 it is defined axiomatically)
factorials = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]  

# so how could we get more of them?
for _n in range(100):  
    factorials.append(factorials[-1] * len(factorials))  
print(f'wow, now we have the first {len(factorials)} in a list:', factorials)


# this is how a function could look like that uses such a list, to return n!
def factorial_list(n):
    # for 0 and 1 the output is just defined as 1
    if n in [0, 1]:
        return 1
    # if n is bigger than 1, we'll create our list
    f_list = [1, 1]
    for i in range(2, n+1):
        f_list.append(f_list[-1]*i)
    # now we can return the last item of the list which is n!
    return f_list[-1]  


# but there is a much smoother way to do that, without any list
# just by calling the same function within the function itself (:= as a recursion)
def factorial(n):  
    if n in [0, 1]:  
        return 1  
    return n * factorial(n-1)  


# we can check now, whether those two functions really do the same thing and
# produce the correct factorials for numbers from 0 to 10
for i in range(10):  
    print(f'factorial({i})      : {factorial(i)}')  
    print(f'factorial_list({i}) : {factorial_list(i)}')

# Ok, so this was an example of what recursion is an can do.
# You might not need it very soon or at all. But keep in mind
# that there are some problems which are better solvable with
# recursion. E.g. a dict, that contains a `data` dict, wich
# again can contain a `data` dict and so on. And you don't know
# how deep this nesting goes. This is one case where recursion
# could come in as quite a handy feature.

'''
Well, this was a lot. Still, there is a lot more. But now
you know all the basics you need to write your own functions.
At some point you might want to split up your functions into
different files, and in your main script import those functions
from those "modules".
But let's leave it for now. If you want to know more, take a look
e.g. at those:
* https://www.w3schools.com/python/python_functions.asp
* https://www.w3schools.com/python/python_modules.asp

Or you want to go full functional? Then take this:
* https://www.w3schools.com/python/python_lambda.asp
But I'd recommend not to overdo it. You don't need to be fully
functional to be an esteemed member of our coding club. ;)
'''

As a file: snippets/functions.py