def identity(a, b): # only positional arguments
return a, b # we can return more than one thing5 Functions
Functions encapsulate pieces of logic that we want to use repeatedly. Also, this encapsulation makes testing our code much easier which is rather useful.
5.1 Basic syntax
identity(1,2)(1, 2)
def identity_with_default(a, b=1): # b is a so called "keyword-argument"
return a, bidentity_with_default("hello") # argument b is optional, if not passed, the default is used('hello', 1)
Python functions always return a value, even without a return statement, in which case the return value will be None.
def implicit_return():
pass # do literally nothingout = implicit_return()out, type(out)(None, NoneType)
These three definitions are equivalent:
def g():
print("hello")
return None
def h():
print("hello")
return
def f():
print("hello")5.2 Arbitrary arguments: *args & **kwargs
We can write a function with an arbitrary (undefined) number of arguments. For that, we use the syntax *args. The args will be put into a tuple:
def print_args(*args):
print(type(args))
for arg in args:
print(arg)print_args(1,2)<class 'tuple'>
1
2
print_args(1, 2, 3, 4) # we can pass as many as we want!<class 'tuple'>
1
2
3
4
This logic extends to key-word arguments. We use the syntax **kwargs for that. Since key-words are pairs, they are put into a dictionary (instead of a tuple):
def print_kwargs(**kwargs): # only positional arguments
for k, v in kwargs.items(): # it's just a dict!
print(k, "->", v)print_kwargs(street="martinistraße", number="52")street -> martinistraße
number -> 52
print_kwargs(street="martinistraße", number="52", coolness="very-high")street -> martinistraße
number -> 52
coolness -> very-high
We can combine both *args and **kwargs. This function will take any arguments we pass:
def general(*args, **kwargs):
print(args)
print(kwargs)general(1, 2, first="hello", second="world")(1, 2)
{'first': 'hello', 'second': 'world'}
Use key-word (named) arguments by default, it will make your code much more readable, maintainable and easier to debug.
5.3 Functions are values
We can assign functions to variables and pass them around, like any other object (people call this to have “functions as first-class citizen” in the language).
def printer(func):
out = func() # call whatever function we pass
print(out) # print the outputdef greeting():
return "hello from greeting func"printer(greeting)hello from greeting func
f = greeting # Notice we are not calling it with ()f<function __main__.greeting()>
printer(f)hello from greeting func
5.4 Anonymous functions
There is a shorthand to define functions with this syntax:
lambda [optional-args]: [return-values]
lambda: "say hello"<function __main__.<lambda>()>
printer(lambda: "hello course")hello course
It can also take arguments:
f = lambda x: x+1f(1)2
A typical use case of anonymous functions:
names = ["anna", "lui", "marco", "ramiro", "tim"]sorted(names, key=lambda name: name[-1])['anna', 'lui', 'tim', 'marco', 'ramiro']
sorted(names, key=lambda name: len(name))['lui', 'tim', 'anna', 'marco', 'ramiro']
ages = [("anna", 93), ("lui", 19), ("marco", 11), ("ramiro", 83)]sorted(ages, key=lambda name_age: name_age[1]) # name_age is a tuple[('marco', 11), ('lui', 19), ('ramiro', 83), ('anna', 93)]
5.5 Early return
Python functions have so-called early return, which means a function will exit as soon as it hits the first return statement, for example:
def early():
if 1 > 0:
return "first condition"
if 2 > 0: # This code will never be reached
return "second condition"early()'first condition'
This can help us to simplify code, for example, this two definitions are equivalent:
def f(x):
if x == 1:
return "it's 1"
elif x == 2:
return "it's 2"
elif x == 3:
return "it's 3"
else:
return "not 1,2,3"
def ff(x):
if x == 1:
return "it's 1"
if x == 2:
return "it's 2"
if x == 3:
return "it's 3"
return "not 1,2,3"f(2), f(4)("it's 2", 'not 1,2,3')
ff(2), ff(4)("it's 2", 'not 1,2,3')
5.6 Keyword-only arguments
There’s a way to force the arguments of a function to be keyword-only, which can be useful to avoid mistakes and kindly nudge the users of our code (yes, you yourself too!) to pass the arguments to a function explicitly.
Everything coming after * must be key-word:
def func(a, b, *, c, d):
print(a, b, c, d)This will not work (pay attention to the error message):
func("hi", "there")--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[4], line 1 ----> 1 func("hi", "there") TypeError: func() missing 2 required keyword-only arguments: 'c' and 'd'
Neither will this:
func("hi", "there", "dear", "students") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[5], line 1 ----> 1 func("hi", "there", "dear", "students") TypeError: func() takes 2 positional arguments but 4 were given
func("hi", "there", "dear", d="students") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[7], line 1 ----> 1 func("hi", "there", "dear", d="students") TypeError: func() takes 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given
Only passing c and d explicitly will do:
func("hi", "there", c="dear", d="students") hi there dear students
5.7 Exercises
- Write a function called
introthat takes two positional arguments, name and age, and prints a sentence introducing a person. The function should return nothing. - Repeat it, but adding an optional argument, city. The function should now return the introducing string and handle the city input too.
- Write a function called
sort_dict_by_valuethat takes a dictionary and returns a dictionary sorted by value. - Write a function that takes an arbitrary number of key-word only arguments representing pairs (name, age) and returns a list of tuples sorted by age. For example:
# For this pairs
your_function(lua=32, mark=12)
# Should output:
[("mark", 12), ("lua", 32)]The very same function should be able to deal with other number of arguments, eg:
your_function(lua=32, mark=12, anna=42)
# Should output:
[("mark", 12), ("lua", 32), ("anna", 42)]Hint: Try to use the function sort_dict that you wrote in the previous point.
- Write a function that takes 2 arguments
items(a container of elements,no matter what they are) andsorting_func(a function that will somehow sort the elements ofitems) and returns the result of the sorting function applied to items. For example, your function should behave like this:
your_function([3, 2, 1], sorted)
# Should output:
[1, 2, 3]
your_function({"a": 2, "b": 1}, sort_dict)
# Should output:
{"b": 1, "a": 2}