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#
(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"})
140534697403712 {'title': 'home'}
140534697403712 {'title': 'home'}
140534697399808 {'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"})
140534697429056 {'title': 'home'}
140534697404800 {'title': 'work'}
140534731659136 {'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"})
140534731793408 {'title': 'home'}
140534731795200 {'title': 'work'}
140534697703744 {'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#
(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#
(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#
(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#
(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#
(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.