Introduction VII - Introduction to Python III
Contents
Introduction VII - Introduction to Python III¶
Michael Ernst
Phd student - Fiebach Lab, Neurocognitive Psychology at Goethe-University Frankfurt
Before we get started …¶
most of what you’ll see within this lecture was prepared by Ross Markello, Michael Notter and Peer Herholz and further adapted for this course by Maren Wehrheim and Michael Ernst
based on Tal Yarkoni’s “Introduction to Python” lecture at Neurohackademy 2019
based on http://www.stavros.io/tutorials/python/ & http://www.swaroopch.com/notes/python
based on https://github.com/oesteban/biss2016 & https://github.com/jvns/pandas-cookbook
Maren Wehrheim
Phd student - FIAS(Frankfurt Institute for Advanced Studies) - Kaschube-Lab and Fiebach Lab, Neurocognitive Psychology at Goethe-University Frankfurt
What we will do in this section of the course is a short introduction to Python
to help beginners to get familiar with this programming language
.
It is divided into the following chapters:
Here’s what we will focus on in this block:
Recap
This session
Recap of the last session¶
Before we dive into new endeavors, it might be important to briefly recap the things we’ve talked about so far. Specifically, we will do this to evaluate if everyone’s roughly on the same page. Thus, if some of the aspects within the recap are either new or fuzzy to you, please have a quick look at the respective part of the first session and second session again and as usual: ask questions wherever something is not clear.
This recap will cover the following topics from the last sessions:
variables and types
operators and comparison
strings, lists and dictionaries
Variables and data types¶
Exercise 1.1¶
Two session ago we declared several variables. Could you here create a variable for the following:
a variable
random_number
with a number between 0 and 10a variable called
my_favorite_food
indicating your favorite food
What data types
do each of these variables have?
# Please write your solution here
operators and comparison¶
Exercise 1.2¶
Please calculate the following and assign
them to the variables
a
, b
, c
, etc., if needed:
10 divided by 3
6 to the power of 2
5 times 29 times 48
Update c by adding 10
Update b by dividing it by 5
Is c divisible by 5?
Is a larger than b?
Are c and b equal?
# Please write your solution here
strings, lists and dictionaries¶
Exercise 1.3¶
You just did an experiment asking 10 people for a random number between 0 and 100. What data structure
would you use to store these values using python?
Assume the values were: 1; 50; 23; 77; 91; 3; 34; 81; 55; 49
. How would you define a variable of your selected data type to store these values?
# Please write your solution here
Exercise 1.4¶
From the above random numbers you now want to calculate the mean and test if that mean is higher than 50. How would you do that?
# Please write your solution here
Exercise 1.5¶
Define a variable rand_num_sub_4
that contains the random number
from the fourth subject
of your experiment and print
the statement 'Subject number four picked X as random number'
, where X
shoudle be replaced using string formatting. Assign the variable using it’s index.
Remember: Where do you start to index in Python?
# Please write your solution here
Exercise 1.6¶
Oh no you made several mistakes! Update
your list
to correct the mistakes.
The
random number
of thefourth participant
wasn’t actually77
but76
.You forgot to keep track of another participant and actually recorded
11 subjects
. You forgot the8th
subject with the number33
. Please add them to your list.You actually don’t want people to choose the number
50
. Pleaseremove
thisvalue
from yourlist
!
# Please write your solution here
Exercise 1.7¶
Instead of only declaring a list containing all the random numbers
, you now want to also assign
a personal ID
to each subject
that you asked. In our case this would simply be Sub_1
to Sub_10
for the first
, second
, etc. subject
, respectively.
Define a dictionary
to store this information. Then print
all the keys
and also redefine
the rand_num_sub_4
variable from before, using your new dictionary
.
# Please write your solution here
Exercise 1.8¶
We’ll assign the inscription on the ring from lord of the rings as a string
to a variable called lord_of_the_rings
. As a reminder, the inscription is:
One Ring to rule them all, One Ring to find them, One Ring to bring them all, and in the darkness bind them.
We also want to create our own fantasy saga, but we are little copy cats. Therefore, we want to define a new statement for the lord of the stones
, and only replace
the word ring
with stone
in our sentence. How would you do that?
# Please write your solution here
Exercise 1.9¶
How long
is this statement now compared
to before?
Further, we want to know what the indices
40 to 45
were in the original inscription
and what they would be in our new sentence
?
# Please write your solution here
Exercise 1.10¶
You can split
a string
into a list
of strings
using string.split(' ')
. This function splits
the string given the parameter ' '
(i.e the whitespaces) and generates a list
. Assign the split list
to a new variable
and print
it.
Split
the lord of the stones string
into a list of words
, sort
this new list
,
add it to a new variable
and print
the sorted list
. Further print
the 10th element
of your sorted list
.
# Please write your solution here
Thanks for taking the time to go through this recap!
If you could solve/answer all questions, you should be well prepared for this session. Otherwise, maybe check out the earlier lessons again or work-out your googling skills!
Indentation & Control Flow¶
Objectives 📍¶
learn about indentation (some form of code structuring)
control how our code is executed
Conditionals (
if
-else
statements)Iteration (e.g.,
for
-loops,while
statements…)
Indentation¶
Python
uses whitespaces
to define code blocks
.
A single whitespaces
at the beginning of a line is consider a simple indent
. A code block
that is indented
with the same number
of leading whitespaces
or tabs
should be run together.
In other words: the indentation
is part of the syntax
in python
and one of the major distinctions regarding other programming languages like, e.g. Matlab
.
Usually in Python
we use four whitespaces
for indentation
of code blocks
(simply hitting the tab
key does the trick). This is defined in the PEP8 Style Guide
under whitespace “ethics”.
Let’s see what that means:
days_til_christmas = 51
current_weekday = 'Saturday'
Each set of these statements is called a block, meaning that the lines/variable
assignments will be run together. We will see examples of how blocks are important later on.
What happens when we introduce a “wrong” indentation
?
days_til_christmas = 51
current_day = 'Saturday'
Cell In [2], line 2
current_day = 'Saturday'
^
IndentationError: unexpected indent
days_til_christmas = 51
current_day = 'Saturday'
Cell In [6], line 2
current_day = 'Saturday'
^
IndentationError: unexpected indent
One thing you should remember is that a wrong indentation
raises an IndentationError
. These can be a lot more impactful than the errors we’ve encountered before, so we won’t adress how to use try - except
to catch these here.
Control Flow & structures¶
Programming languages (i.e. python
) allows us to control
how code is executed
using
Conditionals (
if
-else
statements statements)Iteration (e.g.,
for
-loops,while
statements…)
Conditional statements: if, elif, else¶
The python syntax
for conditional
execution of code
uses the keywords if
, elif
(else if), else
:
To quickly demonstrate, we’ll define two False
statements and see how if
, elif
and else
deal with boolean
s:
statement1 = False
statement2 = False
if statement1:
print("statement1 is True")
elif statement2:
print("statement2 is True")
else:
print("statement1 and statement2 are False")
statement1 and statement2 are False
Note that we’re actually asking if
is True
. The True
boolean
is silent in our conditionals
.
GIF?
We again encounter that:
Program blocks are defined by their indentation level.
In Python, the extent of a code block is defined by the indentation level
(usually a tab
or four white spaces
). This means that we have to be careful to indent our code correctly, or else we will get syntax errors, as demonstrated above.
Examples:
# Good indentation
statement1 = statement2 = True
if statement1:
if statement2:
print("both statement1 and statement2 are True")
both statement1 and statement2 are True
# Bad indentation! This would lead to an error
if statement1:
if statement2:
print("both statement1 and statement2 are True") # this line is not properly indented
Cell In [7], line 4
print("both statement1 and statement2 are True") # this line is not properly indented
^
IndentationError: expected an indented block
Now what does this error tell us?
statement1 = False
if statement1:
print("printed if statement1 is True")
print("still inside the if block")
printed if statement1 is False
still inside the elif block
Oh no! We’re is our output?
Well, let’s try again. This time let’s be explicit in what we’re trying to conditon
on by using the is
keyword
(remember those?)
statement1 = False # still False
if statement1:
print("printed if statement1 is True")
print("still inside the if block")
elif statement1 is False:
print("printed if statement1 is False")
print("still inside the elif block")
printed if statement1 is False
still inside the elif block
If
conditionals
care about indentation
. What happens if we’re not on the same indentation level
?
statement1 = True
statement1 = not False # so True
if statement1:
print("printed if statement1 is True")
print("now outside the if block")
printed if statement1 is True
now outside the if block
Using the or
& and
logic from the last session, we can even combine statements:
print("Statement2 was:", statement2, "right?")
print('So:')
if statement1 and statement2:
print("printed if statement1 and statement2 are True")
elif statement1 or statement2:
print("printed if either statement1 or statement2 is True")
else:
print("printed if no statement is True")
Statement2 was: True right?
So:
printed if statement1 and statement2 are True
Exercise 2.1¶
You want to go to the cinema, but you first need to check whether you have enough money.
First define a variable
indicating the amount of money in your wallet money_in_wallet
as 6 EUR
and the ticket price for the cinema ticket_price
as 10 EUR
.
Indicate with a print
statement IF
you can afford to go to the cinema! Use both if
and else
conditionals
to print wether you’re going out or staying home.
### Write your solution here ###
Exercise 2.2¶
Different films cost different amounts of money. Use if
statements to tests which films
you can afford and print
those. The films cost:
James Bond: 15 EUR
Spider Man - No Way Home: 11 EUR
Dune: 6 EUR
Ghostbusters: 5 EUR
Note: as always in coding, there are several ways to get the right solution.
### Write your solution here ###
Exercise 2.3¶
It’s your lucky day! You happen to find exactly 34 EUR
right in front of your house. You want to celebrate this by inviting as many of your 5 friends
as possible to the cinema.
How many friends can you invite, while also paying for yourself? First update
your money in your wallet
, test
this using if
statements and print
the result.
### Write your solution here ###
Exercise 2.4 - Last one, I promise!¶
This year you want to treat your dog with a christmas present but obviously only if it was a good boy
. Depending on the money in your wallet
, you can either
buy a new toy
for 10 EUR
or instead
go on a nice long walk
if
you don’t have any money.
Write a nested
if
statement to test which present you can buy if your dog was a good boy
. Your current endowment is 11 EUR
.
### Write your solution here ###
These many if
statements were very tedious. Can we not do this more efficiently?
Wait no more. We have a solution (at least for some situations)!
Loops¶
In python
, loops can be programmed in a number of different ways. The most common is the for
loop, which is used together with iterable objects
, such as list
s. The basic syntax
is:
for i in [1,2,3]:
print(i)
1
2
3
But why that i
? Honestly, that is mostly a mixture of laziness and force of habbit. Technically you could give your variable that you iterated over any name that is not a python keyword
or function name
and in general you should be as specific as possible, whenever possible for everyone’s sake.
So let’s try that again:
for num_in_list in [1,2,3]:
print(num_in_list)
1
2
3
Remember that our iterable_variable
will be continously updated. So if we call num_in_list
again, we’ll get the last assigned value, i.e. the last item in our list
.
num_in_list
3
So our for
loop iterates
over the elements
of the supplied list
and executes
the contained code block
once for each element
. Any kind of list can be used in the for
loop.
It’s common to employ the range()
function, which we’ve met in the previous session, to define how many times a loop should repeat.
For example:
for i in range(4): # by default range start at 0
print(i)
0
1
2
3
# to demonstrate you could also create a list using range by
list_range = list(range(4))
list_range
# results in the same as before
for i in list_range:
print(i)
0
1
2
3
print(i)
3
Note: range(4)
does not include 4
! Try to remember the aspects of indexing
and slicing
we addressed during the session on string
s and list
s.
for x in range(-3,3):
print(x)
-3
-2
-1
0
1
2
Again, you can use any kind of list
, so let’s try a list
of strings
:
for word in ["scientific", "computing", "with", "python"]:
print(word)
scientific
computing
with
python
and the variable word
again contains the last item
of the list
we used, i.e. the variable
assumes the value of the last loop iteration
word
'python'
To iterate over key-value pairs
of a dictionary
we need to change up our syntax
a bit. So we include 2 variables
when defining our for loop, like so:
# define a dictionary
params = {
'parameter1': 'A',
'parameter2': 'B',
'parameter3': 'C',
'parameter4': 'D'
}
print(params)
{'parameter1': 'A', 'parameter2': 'B', 'parameter3': 'C', 'parameter4': 'D'}
list(params.items())
[('parameter1', 'A'),
('parameter2', 'B'),
('parameter3', 'C'),
('parameter4', 'D')]
# call the for loop with a variable provided for keys and values
for key in params.items():
print(key)
('parameter1', 'A')
('parameter2', 'B')
('parameter3', 'C')
('parameter4', 'D')
# call the for loop with a variable provided for keys and values
for key in params.keys():
print(key + " = ")
parameter1 =
parameter2 =
parameter3 =
parameter4 =
# or what you'll usually see
for i, j in params.items():
print(i + " = " + str(j))
parameter1 = A
parameter2 = B
parameter3 = C
parameter4 = D
Sometimes it is useful to have access to the indices of the values when iterating over a list
. We can use the enumerate
function for this:
# note that the convention is to call our index varivale `idx`
for idx, x in enumerate(range(-3,3)):
print('loop number = ' + str(idx))
print(x)
loop number = 0
-3
loop number = 1
-2
loop number = 2
-1
loop number = 3
0
loop number = 4
1
loop number = 5
2
break
, continue
and pass
¶
But at times it is necessary to further control how a loop behaves. We’ll therefore introduce a few control statements
To control the flow of a certain loop we’ll use break
, continue
and pass
.
To illustrate this let’s first create another list
rangelist = list(range(10))
print(list(rangelist))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Now if we want our loop to stop running once a condition is met we need our old friends the if
and else
conditional
statements. We’ll evaluate if a certain number is contained in our list and break
the loop once a [4, 5, 7, 9] is found.
for number in rangelist:
# Check if number is one of
# the numbers in the list.
if number in [4, 5, 7, 9]:
# "Break" terminates a for loop without
# executing the "else" clause.
print(number)
print('number ' + str(number) + ' encountered. Loop breaks here!')
break
else:
# "Continue" starts the next iteration
# of the loop. It's rather useless here,
# as it's the last statement of the loop.
print(number)
print('continued')
continue
else:
# The "else" clause is optional and is
# executed only if the loop didn't "break".
pass # Do nothing
0
continued
1
continued
2
continued
3
continued
4
number 4 encountered. Loop breaks here!
Let’s see what continue
and pass
can actually do. Continue
implies that we don’t want our for loop to end, but rather to skip the rest of code block
the for the current iteration. So:
for number in rangelist:
if number in [4, 5, 6]:
print('number ' + str(number) + " encountered. We're supposed to break, right?")
print("oh no the loop can't hear us it has a continue statment in it!")
continue # stop here, continue with next loop
print('so this break statement will never be read?!')
break
elif number in [8, 9]:
print(str(number))
print("break? break!")
break
print('so this continue statement will never be reached?!')
continue
else:
print('else cond = ' + str(number))
else cond = 0
else cond = 1
else cond = 2
else cond = 3
number 4 encountered. We're supposed to break, right?
oh no the loop can't hear us it has a continue statment in it!
number 5 encountered. We're supposed to break, right?
oh no the loop can't hear us it has a continue statment in it!
number 6 encountered. We're supposed to break, right?
oh no the loop can't hear us it has a continue statment in it!
else cond = 7
8
break? break!
What about pass
then? We’ll use it to tell our loop to do nothing
if a certain condition is met. Exciting, i know!
for number in rangelist:
if number in [4, 5, 7, 9]:
print('If statements: number ' + str(number) + ' encountered!')
elif number in [2, 3, 6]:
pass
print('elif_statement: number ' + str(number) + ' encountered!' + ' ...continuing...')
pass
else:
print('else_statement: number ' + str(number) + ' encountered!' + ' ...continuing...')
continue
else_statement: number 0 encountered! ...continuing...
else_statement: number 1 encountered! ...continuing...
elif_statement: number 2 encountered! ...continuing...
elif_statement: number 3 encountered! ...continuing...
If statements: number 4 encountered!
If statements: number 5 encountered!
elif_statement: number 6 encountered! ...continuing...
If statements: number 7 encountered!
else_statement: number 8 encountered! ...continuing...
If statements: number 9 encountered!
This is mostly used as a placeholder for code you haven’t written yet and allows us to avoid syntax errors
for number in rangelist:
if number in [4, 5, 7, 9]:
print('If statements: number ' + str(number) + ' encountered!')
elif number in [2, 3, 6]:
# indented code-block expected, this will lead to an syntax error
else:
print('else_statement: number ' + str(number) + ' encountered!' + ' ...continuing...')
continue
Cell In [9], line 7
else:
^
IndentationError: expected an indented block
Exercise 3.1¶
Use a for loop
to print every even number from 0 to 10
Hint: you can check if a number is even with number % 2 == 0
. Remember the modulo
operator?
### Write your solution here ###
Exercise 3.2¶
Use a for loop
that iterates over all days in december (31)
and always prints
the number of days left until christmas
. This loop should break
once christmas
is reached and should wish you a merry christmas using a print
statement.
Hint: define a variable called christmasday that is assigned before your loop starts
### Write your solution here ###
Exercise 3.3¶
Create a list
of your three most favourite foods and iterate over it. In each iteration print the position
(aka index
) and the food
. For example:
Pasta
Pizzas
Wraps
Hint: all necessary commands can be found in this script
### Write your solution here ###
List comprehensions: Creating lists using for
loops:
Now you might encounter some strange looking loops when looking for code online. To keep code sparse and simple looking programmers may sometimes opt to use a single line instead of an indented block for their loops by using list comprehensions
:
A convenient and compact way to initialize lists would then look like this:
We’ll use it to square
every number x
in a list
containing the numbers 0 - 4 and add those to a new list
called list_x_squared
:
list_x_squared = [x**2 for x in range(0,5)]
print(list_x_squared)
[0, 1, 4, 9, 16]
You can also use an if statement
in your list comprehension
. But be careful with the ordering
. A single if can be after the for loop. However, if and else together have to be in front. Lets see some examples.
# if our x is greater than 2, run the list comprehension
list_2 = [x**2 for x in range(0,5) if x > 2]
print(list_2)
[9, 16]
list_3 = [x**2 for x in range(0,5) if x > 2 else x]
print(list_3)
Cell In [31], line 1
list_3 = [x**2 for x in range(0,5) if x > 2 else x]
^
SyntaxError: invalid syntax
Now lets put the if-else statement before the for
loop
list_3 = [x**2 if x > 2 else x for x in range(0,5)]
print(list_3)
[0, 1, 2, 9, 16]
Don’t be discouraged if that looks confusing, you’ll get the hang of it once you actually start working or fiddling with list comprehensions
. Further, it is always fine to use the explicit loop structure, we’ve learned before!
Exercise 3.4¶
Use a list comprehension
to create a list
containing all letters
that are in the word ‘human’.
As always, there are different ways to solve this
### Write your solution here ###
Exercise 3.5¶
Use a list comprehension
to create a list
with all even numbers from 0 to 19
.
Where would you use your if
statement?
Exercise 2.6¶
Use a list comprehension
to create a new list
with all uneven numbers from 0 to 19
and set all even numbers to 0
.
Hint: Probably a good idea to use both if
and else
. In the End the list should still have a length of 20.
### Write your solution here ###
while
loops:¶
Sometimes we do not have a certain iterable that we want or can use for our loops. Enter the while loop
.
A while loop
is used when you want to perform a task indefinitely
, until a particular condition
is met. It is s a condition-controlled loop
.
If we for example want to print all number below 5, we simply do:
i = 0
while i < 5:
print(i)
i = i + 1
print("done")
2
3
4
done
Note that the print "done"
statement is not part of the while
loop body because of the difference in the indentation.
But what is the i = i + 1
code block? Check back in the first python lesson for a qucik refresher, if necessary.
For our next trick, we’ll be a bit more explicit in our variable naming:
counter = 0
while counter < 5:
print(counter)
counter = counter + 1
print("done")
0
1
2
3
4
done
So the i = i + 1
updates our conditional variable on each iteration by 1 + it’s previous value.
If you would forget the i = i+1
expression and our conditional variable would never be updated, we’d be stuck in our while loop
forever, as the condition is never set to not True
. Therefore while
loops can be dangerous to use, if you’re not careful.
# if you run this cell you will need to stop the kernel, as it is an infinite loop
# i = 0
# while i < 5:
# print(i)
You can also include an else
statement after your while
loop, to include a codeblock that should be executed once the condition is False
counter = 0
while counter < 3:
print("Inside loop")
counter = counter + 1
else:
print("Inside else")
Inside loop
Inside loop
Inside loop
Inside else
Exercise 3.7¶
Calculate the number of friends
that you can invite to the cinema using a while
-loop.
Remember:
money = 43
ticket_price = 10
Hint: You may want to alter your money to calculate this, by updating the “money” variable as demonstrated above.
### Write your solution here ###
Functions¶
Careful rule of thumb for great programming
: “Whenever you copy-paste while coding, you do something wrong.’
We use functions
to solve problems that are repetitive
.
What should you put into a function
:
Anything, that you will do more than once
All
code blocks
that have some kind ofmeaning
, e.g. calculating thesquare root
of avalue
Code
that can be increased in readability, where you would like to add a comment
A function
…
is a
block of code
that only runs when explicitly calledcan accept
arguments
(orparameters
) thatalter
itsbehavior
can accept any number/
type
ofinputs
, but alwaysreturn
asingle object
Note: functions
can return tuples
(may look like multiple objects
)
A function in Python
is defined using the keyword def
, followed by a function name
, a signature
within parentheses ()
, and a colon :
. The following code
, with one additional level of indentation
, is the function body
. The return
statement at the end of the function defines what the ouput
will be, when the function is called.
def say_hello():
# block belonging to the function
print('hello world')
say_hello() # call the function
hello world
The function
above does not take any arguments
but only executes the code
within. As there is no explicit return statement
the function will simply return None
.
Let’s check that. To access the return value
of a function
, you simply assign
the function
to a variable
, as we learned above and in the previous sessions.
greetings = say_hello()
hello world
print(greetings)
None
As you see, the function
still printed the 'hello world'
but the returned value
that was stored in greetings
is None
.
A function
can also accept arguments
within the parentheses
, making it super flexible. For example:
def calc_power(x, p):
# x is our base
# p is our exponent
power = x ** p
return power
25
27
Now we can input
specific terms or variables
into our function, like so:
print(calc_power(5,2))
print(calc_power(3,3))
25
27
So the input arguments
take the place of the placeholder variables we x
and p
we used when we initially defined our funtion. What happens if our input arguments are of a different variable type then expected in the function body?
print(calc_power(5,'string'))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [9], line 1
----> 1 print(calc_power(5,'string'))
Cell In [5], line 4, in calc_power(x, p)
1 def calc_power(x, p):
2 # x is our base
3 # p is our exponent
----> 4 power = x ** p
5 return power
TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'str'
Here we get a type error
that explicitly references the function (compare the error output to the above defined function) and tells us that a string can’t be used for the mathematical operation we’re trying to use in line 4
of our function.
What happens if we’re missing an input argument?
print(calc_power(5))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [8], line 1
----> 1 print(calc_power(5))
TypeError: calc_power() missing 1 required positional argument: 'p'
Again, a rather explicit error. Our function was defined as requiring 2 input arguments
, so we need to provide exactly that number.
As we used a return statement
this time, the power
that we calculated is returned
and can be printed
or assigned
to a variable
.
five_to_pow_2 = calc_power(5,2)
print(five_to_pow_2)
25
These were all simple examples, however, we can also put more complex code blocks
into a function
.
Let’s define a function that tells us if a certain input argument
is larger than another input argument
or if they are equal.
def get_maximum(a, b):
maximum = None
if a > b:
print( a, 'is maximum')
maximum = a
elif a == b:
print(a, 'is equal to', b)
maximum = a
else:
print(b, 'is maximum')
maximum = b
return maximum
4 is maximum
7 is equal to 7
Now let’s call that function using the input arguments
3 and 4
# directly pass literal values
maximum = get_maximum(3, 4)
4 is maximum
and that value is now stored in our variable called maximum for future use
maximum
4
We can also use variables
as input arguments, which is useful in larger code bodies
x = 7
y = 7
# pass variables as arguments
maximum = get_maximum(x, y)
7 is equal to 7
maximum
7
We can also return
multiple values
from a function
using tuples
:
def calc_powers(x):
"""
Return a few powers of x.
"""
return x ** 2, x ** 3, x ** 4
x = 5
powers = calc_powers(x)
print(powers)
(25, 125, 625)
As you see, your output variable
powers
is a tuple
that contains the output values
of the function
. We can however, split this tuple
directly into the specific values
. This can help to make your code
more readable. Make sure to speficy the same number of variables to assign to as values your return statements provide. Makes sense?
x2, x3, x4 = calc_powers(5)
print(x3)
125
Very important:
Variables
inside a function
are treated as local variables
and therefore don’t interfere with variables
outside the scope of the function
. This further means that a variable that is defined in a function is not defined outside of the function.
To demonstrate let’s first define a function using the loca variable x
:
def func(x):
print('x is', x)
x = 2
print('Changed local x to', x)
Now we’ll define a variable called x
as we would usually do it by assigning a value to x
x = 50
print(x)
50
Now we call our function, which takes the x
that we’ve just defined outside of the function as an input
func(x)
x is 50
Changed local x to 2
Via the to print
statements in the function we see that indeed takes our x
as input and prints
it’s previously assigned value, i.e. 50. Now in the function body the value of the local x
variable is changed to x = 2
.
Let’s see what that means for our initial x
variable
print(x)
50
So the function-internal x
variable and the externally defined x
variable are to different instances.
To change our so called global
variable, we could implement a return
statement as shown above and assign the function output to the x
variable
Another way to get the same result is to access a local function variable
, by extending it’s "local scope"
with the keyword global
.
x = 50
def func():
global x
print('x is', x)
x = 2
print('Changed global x to', x)
func()
print('Value of x is', x)
x is 50
Changed global x to 2
Value of x is 2
It’s generally best to avoid such ambiguity, but sometimes it’s necessary to access the global
variable inside of functions.
Optionally, but highly recommended, we can define a so called "docstring"
, which is a description
of the functions
purpose and behavior. The docstring
should follow directly after the function definition
, before the code
in the function body
.
You can also define the input
and return
parameters
in the docstring
. There are several conventions
to do this. You can find them here.
def func_with_docstring(s):
"""
Print a string 's' and tell how many characters it has
:param s (string): input string of any length
:returns: None
"""
print(s + " has " + str(len(s)) + " characters")
Which now allows us to use the help function, to understand what the function is supposed to be doing
help(func_with_docstring)
Help on function func_with_docstring in module __main__:
func_with_docstring(s)
Print a string 's' and tell how many characters it has
:param s (string): input string of any length
:returns: None
func_with_docstring('So much fun to write functions')
So much fun to write functions has 30 characters
Positional vs. keyword arguments¶
Positional arguments
are defined byposition
and must be passedArguments
in thefunction signature
are filled in order
Keyword arguments
have adefault value
Arguments
can be passed in arbitrary order (after anypositional arguments
)
You might not think that at the moment, but coding
is all about readability. You only write it once, but you will probably read it several times.
In Python
we can increase readability when calling a function
by also naming our positional arguments
. For example:
def calc_power(x, power):
return x ** power
We could now simply input the variables that we want to compute:
calc_power(2, 2)
4
The input in the function is positionally defined. Hence, the first parameter represents x
and the second power
. But does this really tell you what is happening, just from reading the function
?
We can increase readability by calling the parameters
by their names:
calc_power(x=2, power=2)
4
Now everyone that looks at your code, can directly see what is happening. If we explicitly list the name of the arguments
in the function calls
, they do not need to come in the same order as in the function definition
. This is called keyword arguments
and is often very useful in functions
that take a lot of optional arguments
.
Additionally we could also give default values
to the arguments
the function
takes:
def calc_power(x, power=2):
return x ** power
If we don’t provide a value
for the power
argument when calling the the function calc_power
it defaults
to the value
provided in the function definition
:
x = 3
calc_power(x)
9
Such default values
are especially useful for functions
that take a lot of arguments
, as it reduces the amount of arguments
that you have to pass to the function
.
Arguments
with a default value
…
have to be
defined
/called AFTER thepositional values
don’t have to be called in order
Let’s quickly talk about function names…¶
Function names
are very important. They will allow you to tell the story of your code. Time spent on naming is never wasted time in coding
.
The ‘pythonic’ way to write functions
is to also imply in the name what the function
will do.
Some examples:
when you calculate something you can use
calc
in yourfunction name
.when you want to test if something is
true
with yourfunction
you could useis
. E.g.,is_above_value(x, value=10)
could returnTrue
if theinput value
x
is above thedefault value
.use
print
if yourfunction
onlyprints
statements
Argument unpacking with *args
and *kwargs
¶
Sometimes functions need to accept arbitrary/unknown arguments..
Python handles this elegantly via the
*args
and**kwargs
conventions*args
: a list of all unassignedpositional
arguments**kwargs
: a dictionary of all unassignedkeyword
arguments
Note: the names args and kwargs are purely conventional. The important thing are to use a single *
and some variable name to indicate args and two, i.e. **
to inidcate kwargs
So next we’ll try to understand what that actually means by playing around with a new function called print_args_and_kwargs()
# https://github.com/neurohackademy/introduction-to-python/blob/master/introduction-to-python.ipynb
def print_args_and_kwargs(*args, **kwargs):
print("Args:", args)
print("Kwargs:", kwargs)
The great thing is that *args
allows us to pass a varying number of positional arguments, instead of us being limited to the amount of arguments we specified when we defined functions before.
Let’s try that:
print_args_and_kwargs([1,2,3],[3,4,5], '17', 'float')
Args: ([1, 2, 3], [3, 4, 5], '17', 'float')
Kwargs: {}
**kwargs
does much the same, except it allows us to pass any number of keyword
arguments to a function, so:
print_args_and_kwargs(name='michael', number='017xxx', adress='5G.103', e_mail='ernst@psych')
Args: ()
Kwargs: {'name': 'michael', 'number': '017xxx', 'adress': '5G.103', 'e_mail': 'ernst@psych'}
notice that **kwargs
procudes a key - value pair
for each input parameter
We can now use this in combination with return
to assign the input arguments
transformed in structure to a variable:
def return_args(*args):
print("Args:", args)
return args
args = return_args([1,2,3], 7, 'str')
type(args)
Args: ([1, 2, 3], 7, 'str')
tuple
The *args
function provides a tuple
, what do you believe a **kwargs
may produce?
def return_kwargs(**kwargs):
print("kwargs:", kwargs)
return kwargs
kwargs = return_kwargs(name='michael', number='017xxx', adress='5G.103', e_mail='ernst@psych')
print(kwargs)
print(type(kwargs))
kwargs: {'name': 'michael', 'number': '017xxx', 'adress': '5G.103', 'e_mail': 'ernst@psych'}
{'name': 'michael', 'number': '017xxx', 'adress': '5G.103', 'e_mail': 'ernst@psych'}
<class 'dict'>
For a more in-depth explanation and examples check out the tutorial by geeks for geeks
Exercise 4.1¶
Define a function
called get_longest_string
that takes two strings
as input
and returns
the longer string
. If the strings
have the same length
, the first
one should be returned
.
Call your function
once, using positional arguments
and once using keyword arguments
.
## Write your solution here
Exercise 4.2¶
Define a function
named happy_holidays
that wishes the user happy holidays.
It takes the name of the user
as input
argument
.
When the user
does not define a name, your function
should use a default value
.
Then call it once, inputting
a user name
and once without.
## Write your solution here
Homework assignment #5¶
Your fifth homework assignment will entail working through a few tasks covering the contents discussed in this session within of a jupyter notebook
. You can download it here. In order to open it, put the homework assignment notebook
within the folder you stored the course materials
, start a jupyter notebook
as during the sessions, navigate to the homework assignment notebook
, open it and have fun! NB: a substantial part of it will be optional and thus the notebook will look way longer than it actually is.
Deadline: 15/01/2023, 11:59 PM EST
Also, in preparation for our switch to more applied things, i.e. starting with experiments in python
. We’ll have to install psychopy
. To do that please download the programm from here and follow the instructions for your specific operating system. If you encounter any Problems, please let me know!