Functions
Contents
Functions#
In this lesson you’ll learn about some slightly more advanced or obscure function writing.
Important
Be sure to complete the fundamentals Functions lesson first.
Table of Contents
Part 1: Optional arguments#
Part 1.1: Default Values#
You can make an argument optional by giving it a default value.
def hr(width=20):
print("-" * width)
When you call the function, you can pass an argument as you normally would.
hr(10)
----------
Or skip the argument to use the default value.
hr()
--------------------
Any parameters without default values have to go before any with default values.
def hr(char, width=20):
print(char * width)
Part 1.2: Exercise#
Exercise 88 (Defaults)
Define a function write()
that takes three arguments:
message
before
with a default of0
after
with a default of0
The function should print message
with the specified number of blank lines
before
and after
, then print the string "----"
at the end.
Call it with all variations of missing and present default arguments.
Solution to Exercise 88 (Defaults)
1def write(message, before=0, after=0):
2 print("\n"*before, end="")
3 print(message)
4 print("\n"*after, end="")
5 print("----")
6
7write("hello")
8write("bonjour", after=1)
9write("hola", before=1)
10write("aloha", before=1, after=1)
Part 1.3: Mutable Defaults#
Avoid using a mutable object as a default value, since it is evaluated only once when the function is defined.
def setup(project, options={}):
if "title" not in options:
options["title"] = project
print(id(options), options)
Therefore any call that uses the default value will have the exact same object.
setup("home")
setup("work")
setup("school", {"title": "School Work"})
140704750409536 {'title': 'home'}
140704750409536 {'title': 'home'}
140704750405504 {'title': 'School Work'}
For mutable types, set the default value to None
. Then in the body of the
function, assign the value if the argument is falsy.
def setup(project, options=None):
if not options:
options = {}
if "title" not in options:
options["title"] = project
print(id(options), options)
This will ensure that every time the function is called, a new value is created and assigned.
setup("home")
setup("work")
setup("school", {"title": "School Work"})
140704750707136 {'title': 'home'}
140704750410624 {'title': 'work'}
140704751106560 {'title': 'School Work'}
A shorthand for this is:
NAME = NAME or DEFAULT
def setup(project, options=None):
options = options or {}
if "title" not in options:
options["title"] = project
print(id(options), options)
setup("home")
setup("work")
setup("school", {"title": "School Work"})
140704750405760 {'title': 'home'}
140704751248704 {'title': 'work'}
140704751248576 {'title': 'School Work'}
Part 2: Arbitrary arguments#
Sometimes you want a function to be able to take an arbitrary arguments. These are called variadic arguments.
Part 2.1: Positional#
This can be done with a single asterisk before a parameter name. Any
positional arguments will then be in a tuple with that name, in this case
args
.
def thing(*args):
print(args)
if args:
print(args[0])
You can then call it with no arguments…
thing()
()
One argument…
thing("a")
('a',)
a
Or any number of arguments…
thing("a", "b", "c", "d", "e")
('a', 'b', 'c', 'd', 'e')
a
If you mix positional arguments and variadic the positional arguments must be first in the function definition.
def thing(stuff, *args):
print(stuff, args)
if args:
print(args[0])
Arguments with default values go after *args
.
def thing(stuff, *args, data=None):
print(stuff, args, data)
if args:
print(args[0])
Note that when calling a function that like this you’ll need to use
keyword arguments to specify the parameter name of any arguments with
default values. If you send it as a positional argument, it will be part of
args
.
thing("abc", [1, 2, 3])
abc ([1, 2, 3],) None
[1, 2, 3]
thing("abc", data=[1, 2, 3])
abc () [1, 2, 3]
Part 2.1: Exercise#
Exercise 89 (Arbitrary Positional Arguments)
Modify your write()
so that it takes an arbitrary number of positional
arguments instead of message. It should converts all to strings then join them
with a space between each before printing.
Example Usage
>>> write("abc", 123)
abc, 123
----
Solution to Exercise 89 (Arbitrary Positional Arguments)
1def write(*args, before=0, after=0):
2 args = map(str, args)
3 message = " ".join(args)
4
5 print("\n"*before, end="")
6 print(message)
7 print("\n"*after, end="")
8 print("----")
Part 2.2: Keyword#
To take arbitrary keyword arguments, put two asterisks before a parameter name.
def thing(**kwargs):
print(kwargs)
if "a" in kwargs:
print(kwargs["a"])
The keyword arguments will then be in a dictionary with that name, in this case
kwargs
.
thing(a=1, b=2, c=3)
{'a': 1, 'b': 2, 'c': 3}
1
Variadic keyword arguments go after parameters with default values in the function definition.
def thing(stuff, *args, data=None, **kwargs):
print(stuff, args, data, kwargs)
if args:
print(args[0])
Part 2.2: Exercise#
Exercise 90 (Arbitrary Keyword Arguments)
Modify write()
to also take arbitrary keyword arguments. Each keyword
argument should be formatted into a string: NAME=VALUE
, then
printed (along with any arguments) seperated by commas.
Example Usage
>>> write("abc", 123, a=1, b=2.0, c="three")
abc 123 a=1 b=2.0 c='three'
----
Solution to Exercise 90 (Arbitrary Keyword Arguments)
1def write(*args, before=0, after=0, **kwargs):
2 args = [str(x) for x in args]
3 args += [f"{k}={v!r}" for k,v in kwargs.items()]
4 message = " ".join(args)
5
6 print("\n"*before, end="")
7 print(message)
8 print("\n"*after, end="")
9 print("----")
Part 3: Unpacking Arguments#
Sometimes want a function to take arbitrary arguments, sometimes you want to send all of the values in a list or dictionary as arguments to a function. This is known as unpacking.
Part 3.1: Sequences#
To send all elements in a list, tuple, or other sequence, put an asterisk before the object.
birth_stones = [
"Garnet",
"Amethyst",
"Aquam",
"Diamond",
"Emerald"
]
print(*birth_stones)
Garnet Amethyst Aquam Diamond Emerald
You can unpack more than one sequence.
birth_stones = [
"Garnet",
"Amethyst",
"Aquam",
"Diamond",
"Emerald"
]
flowers = (
"Carnation",
"Violet",
"Jonquil",
"Sweet Pea",
"Lily"
)
print(*birth_stones, *flowers)
Garnet Amethyst Aquam Diamond Emerald Carnation Violet Jonquil Sweet Pea Lily
All unpacked sequences must go before any keyword arguments.
birth_stones = [
"Garnet",
"Amethyst",
"Aquam",
"Diamond",
"Emerald"
]
flowers = (
"Carnation",
"Violet",
"Jonquil",
"Sweet Pea",
"Lily"
)
print("Stones and flowers:", *birth_stones, *flowers, sep="\n")
Stones and flowers:
Garnet
Amethyst
Aquam
Diamond
Emerald
Carnation
Violet
Jonquil
Sweet Pea
Lily
Part 3.2: Exercise#
Exercise 91 (Unpacking Sequences)
Make a list of cities you’ve lived in. Print them by unpacking the list as
arguments to the print()
function. Bonus: Mix with positional and keyword
arguments.
Solution to Exercise 91 (Unpacking Sequences)
1cities = [
2 "Oceanside, CA",
3 "San Francisco, CA",
4 "Lafayette, CA",
5 "Denver, CO",
6]
7
8print(*cities)
9print("Cities I've lived in:", *cities, sep="\n")
Part 3.3: Mappings#
To send all elements of a dictionary as keyword arguments, put two asterisks before the dictionary.
def show(red, green, blue, hex_code):
print(f"Hex Color: #{hex_code} is RGB: ({red}, {green}, {blue})")
color = {
"hex_code": "21abcd",
"red": 33,
"green": 171,
"blue": 205,
}
show(**color)
Hex Color: #21abcd is RGB: (33, 171, 205)
As with unpacking sequences, you can unpack multiple dictionaries.
color = {
"hex_code": "21abcd",
}
rgb = {
"red": 33,
"green": 171,
"blue": 205,
}
show(**color, **rgb)
Hex Color: #21abcd is RGB: (33, 171, 205)
The unpacked keyword arguments must go after any positional arguments.
color = {
"green": 0,
"blue": 0,
}
show(255, **color, hex_code="#FF0000")
Hex Color: ##FF0000 is RGB: (255, 0, 0)
Part 3.4: Exercise#
Exercise 92 (Unpacking Mappings)
Make a dictionary called options
that includes some of the values for sep
,
end
or file
. Unpack options
it as arguments to print()
after printing
your previous list of cities.
Solution to Exercise 92 (Unpacking Mappings)
1options = {"sep": "; ", "end": "\n\n"}
2print(*cities, **options)
Part 4: Annotations#
Python provides a syntax for documenting the type of various values called annotations. While annotations have no effect on how a function behaves, it can be helpful in clarifying the intended usage.
Part 4.1: Parameter Hints#
You can specify the expected type of each parameter by adding a colon after the parameter name, followed by the type.
def debug(message: str):
print(message)
Part 4.2: Variable Hints#
Incidentally, you can specify the type of a particular variable the same way.
maximum: int = 100
name: str
Part 4.3: Return hints#
You can specify the type of any returned value by adding ->
followed by the
type before the colon.
from random import randint
def random(limit: int=10) -> int:
return randint(1, limit)
Be aware that is strictly documentation. An annotation does not change or enforce the type of a given value.
Part 4.4: Exercise#
Exercise 93 (Annotations)
Write a function get_keys()
that takes two arguments: source
(dict) and
keys
(list). It should return a dictionary containing all key value pairs
from source
for keys in keys
.
Annotate both the parameters and return value.
** Example Usage**
1>>> import string
2>>> alpha = dict(zip(string.ascii_lowercase, range(1, 27)))
3
4>>> get_keys(alpha, ["x", "y", "x"])
5{'x': 24, 'y': 25, 'z': 26}
Solution to Exercise 93 (Annotations)
1def get_keys(source: dict, keys: list) -> dict:
2 return {k: source.get(k) for k in keys}
Part 5: Lambdas#
Python provides an inline syntax for short functions that return the results of a single expression called lambdas.
def random():
return randint(1, 100)
random = lambda: randint(1, 100)
def random(limit):
return randint(1, limit)
random = lambda limit: randint(1, limit)
Part 6: Shorthand#
It is possible, though not recommended, to write a function (or any other compound statement) all on one line, assuming that it contains a single line.
The following two functions are equivalent.
1def hello():
2 print("hello")
1def hello(): print("hello")
Reference#
Glossary#
Functions#
- variadic#
- variadic arguments#
When any number of arguments may be passed to a function or method.
- annotations#
- type hints#
Syntax for specifying the type of a variable, class attribute, function parameter or return value.
- lambda#
An anonymous inline function consisting of a single expression.
- unpacking#
- unpacking arguments#
Sending all elements in a collection as arguments to a function call.