Python101/Session 3 - Functions/Session 3 - Functions.html

294 KiB

<html> <head> </head>

Session 3

Functions

A function is a block of code that is first defined, and thereafter can be called to run as many times as needed. A function might have arguments, some of which can be optional if a default value is specified. A function is called by parantheses: function_name(). Arguments are placed inside the paranthes and comma separated if there are more than one. Similar to f(x, y) from mathematics.

A function can return one or more values to the caller. The values to return are put in the return statement, which ends the function. If no return statement is given, the function will return None.

The general syntax of a function is:

def function_name(arg1, arg2, default_arg1=0, default_arg2=None):
    '''This is the docstring 

    The docstring explains what the function does, so it is like a multi line comment. It does have to be here, 
    but it is good practice to use them to document the code. They are especially useful for more complicated 
    functions.
    Arguments could be explained togehter with their types (e.g. strings, lists, dicts etc.).
    '''

    # Function code goes here

    # Possible 'return' statement terminating the function. If 'return' is not specified, function returns None.
    return return_val1, return_val2

If multiple values are to be returned, they can be separeted by commas. The returned entity will by default be a tuple.

Note that when using default arguments, it is good practice to only use immutable types as defaults. An example further below will demonstrate why this is recmommended.

Basic functions

A simple function with one argument is defined below.

In [1]:
def f(x):
    return 6.25 + x + x**2

Note: No code has been executed yet. It has merely been defined and ready to run when called.

Calling the function with an argument returns:

In [3]:
print(f(5))
36.25

If we define a function without returning anything, it returns None:

In [7]:
def first_char(word):
    word[0]    # <--- No return statment, function returns None
    

a = first_char('hello')   # Variable a will be equal to None
print(a)
None

Often a return value is wanted from a function, but there could be scenarios where it is not wanted. E.g. if you want to mutate a list by the function. Consider this example:

In [12]:
def say_hello_to(name):
    ''' Say hello to the input name  '''
    print(f'Hello {name}')
    

say_hello_to('Anders')           # <--- Calling the function prints 'Hello {name}'
r = say_hello_to('Anders')
print(r)                         # <--- Prints None, since function does not return anyhing 
Hello Anders
None

The function was still useful even though it did not return anything.

Local vs. global variables

  • Global variables: Variables defined outside a function
  • Local variables: Varaiables defined inside a function

Local variables cannot be accessed outside the function. By returning a local variable and saving it into a global variable we can use the result outside the function, in the global namespace.

Default arguments

A little side note

A good practice is to use only immutable types as default arguments. Python will allow mutable arguments to be given as defaults, but the behavior can be confusing if the default argument is mutated within the function.

The enumerate function

The enumerate function is useful whenever you want to loop over an iterable together with the index of each value:

In [18]:
letters = ['a', 'b', 'c', 'd', 'c']
for idx, letter in enumerate(letters):
    print(idx, letter)
0 a
1 b
2 c
3 d
4 c
In [23]:
for idx, letter in enumerate(letters, start=1):   # Starting at 1 (internally, enumerate has start=0 set as default)
    print(idx, letter)
1 a
2 b
3 c
4 d
5 c

The zip function

The zipfunction is useful when you want to put two lists up beside eachother and loop over them.

In [22]:
diameters = [10, 12, 16, 20, 25]                    
areas = [3.14 * (d/2)**2 for d in diameters]

for d, A in zip(diameters, areas):
    print(d, A)
10 78.5
12 113.04
16 200.96
20 314.0
25 490.625

Imports

Libraries

A quick overview of imports of libraries in Python, here shown for the math library:

import math            # Let's you access everything in the math library by dot-notation (e.g math.pi)  
from math import pi    # Let's you use pi directly
from math import *     # Let's you use everything in the math library directly

The last one is not considered good practice, since varaibles will be untracable. It can be good for making quick tests though.

Your own modules

You can also import your own .py files this way and access the functions inside them. It is easiest if the file to import is located in the same folder as the .py file you want to import to. Note that Python files .py are normally called modules.

An example:

import my_module      # my_module could be your own python file located in same directory

If you have a function inside my_module called my_func, you can now call it as my_module.my_func().

Exercise 1

Finish the function below that takes a radius as input and make it return the circle area.

def circle_area(r):
    '''Return circle area'''
    # Your code goes here

Try to call it to see if it works. If you wan to access pi to avoid typing it out yourself, put the line from math import pi at some point before calling the function.

Exercise 2

Write a function that takes a list radii as input and returns a list of the corresponding circle areas. Try to set it up from scrath and test it.

You can use the function from the previous exercise if you want.

Exercise 3

Write the function described in the docstring below.

def is_pile_long(pile_lengths):
   ''' Return a list with elements `True` for all piles longer than or equal to 5m, `False` otherwise.

   Args:
       pile_length : A list containing pile lengths   

   Example: 
       is_pile_long([[4.51, 6.12, 4.15, 7.31, 5.01, 4.99, 5.00]])
           ---> [False, True, False, True, True, False, True] 
   '''
   # Your code goes here

Exercise 4

Finish the function below so it does as described in the docstring. Remember to import the sqrt function from the math module.


def dist_point_to_line(x, y, x1, y1, x2, y2):
    '''Return distance between a point and a line defined by two points.

    Args:
        x  : x-coordinate of point 
        y  : y-coordinate of point
        x1 : x-coordinate of point 1 defining the line
        y1 : y-coordinate of point 1 defining the line
        x2 : x-coordinate of point 2 defining the line
        y2 : y-coordinate of point 2 defining the line

    Returns:
           The distance between the point and the line        
    '''
    # Your code goes here

The distance between a point $(x, y)$ and a line passing through points $(x_1, y_1)$ and $(x_2, y_2)$ can be found as

\begin{equation*} \textrm{ distance}(P_1, P_2, (x, y)) = \frac{|(y_2-y_1)x - (x_2-x_1)y + x_2 y_1 - y_2 x_1|}{\sqrt{ (x_2-x_1)^2 + (y_2-y_1)^2 }} \end{equation*}

Call the function to test if it works. Some examples to test against:

dist_point_to_line(2, 1, 5, 5, 1, 6)                      -->   4.61 
dist_point_to_line(1.4, 5.2, 10.1, 2.24, 34.142, 13.51)   -->   6.37

Exercise 5

Given a line defined by points $(x_1, y_1)=(2, 3)$ and $(x_2, y_2)=(8, 7)$, compute the distance to the points with the coordinates below and put the results into a list

x_coords = [4.1, 22.2, 7.7, 62.2, 7.8, 1.1]
y_coords = [0.3, 51.2, 3.5, 12.6, 2.7, 9.8]

You can either use a list comprehension or create a traditional for loop where results get appended to the list in every loop.

Exercise 6

Create a function that calculates the area of a simple (non-self-intersecting) polygon by using the so-called Shoelace Formula

$$ A_p = \frac{1}{2} \sum_{i=0}^{n-1} (x_i y_{i+1} - x_{i+1} y_i) $$

The area is signed depending on the ordering of the polygon being clockwise or counter-clockwise. The numerical value of the formula will always be equal to the actual area.

The function should take three input parameters:

  • xv - list of x-coordinates of all vertices
  • yv - list of y-coordinates of all vertices
  • signed - boolean value that dictates whether the function returns the signed area or the actual area. Default should be actual area.

Assume that the polygon is closed, i.e. the first and last elements of xv are identical and the same is true for yv.

A function call with these input coordinates should return 12.0:

x = [3, 4, 7, 8, 8.5, 3]
y = [5, 3, 0, 1, 3, 5]

Source: https://en.wikipedia.org/wiki/Polygon#Area

Exercise 7

Write a function that calculates and returns the centroid $(C_x, C_y)$ of a polygon by using the formula:

$$ C_x = \frac{1}{6A} \sum_{i=0}^{n-1} (x_i+x_{i+1}) (x_i y_{i+1} - x_{i+1} y_i) $$$$ C_y = \frac{1}{6A} \sum_{i=0}^{n-1} (y_i+y_{i+1}) (x_i y_{i+1} - x_{i+1} y_i) $$

x and y are lists of coordinates of a closed simple polygon.

Here, $A$ is the signed area. When you need $A$, call the function from the previous exercise to get it. Be sure to call it with the non-default signed=True.

A function call with the input coordinates below should return (6.083, 2.583):

x = [3, 4, 7, 8, 8.5, 3]
y = [5, 3, 0, 1, 3, 5]

Source: https://en.wikipedia.org/wiki/Centroid#Of_a_polygon

If you are up for more

Try to write a function that can calculate the elastic centroid of a section with materials of two different stiffness values. E.g. a reinforced polygon concrete section.

</html>