Writing and Using Functions

Functions are at the heart of most Python packages. While it is possible to write single use scripts to conduct a variety of simple analytical applications, many more complex applications rely on functions.

You can think of a function as a tiny (or potentially large) package of re-usable code, which can be applied multiple times to conduct similar tasks. Even if you do not plan to write functions yourself, it is useful to know how they work and how to read them, as this will help you be understand how to use functions you find in widely used packages or in code you are working with.

Defining Functions

Functions can be declared with the def keyword. This is followed by the name of the function, and a set of parenthesis that defines the arguments that are inputs to the function. If there are no arguments, you can put an empty set of parenthesis.

[1]:
def greeting():
    print("Hello, Florida!")

To use a function, you simply use the name of the function (the part that comes immediately after def), followed by a set of parenthesis that contains any arguments. If you are not giving any arguments to the function, you still need the set of parenthesis, even if it is empty.

[2]:
greeting()
Hello, Florida!

Function Arguments

Multiple arguments can be defined, and when the function is called the arguments are expected in the same order that they appear in the function definition.

The return keyword is used to identify the result. This value is returned as the output of the function.

[3]:
def to_kilometers(miles, feet):
    distance = miles + feet/5280
    return 1.60934 * distance

to_kilometers(26, 1155)
[3]:
42.194883125

If you define arguments like above, then the function must always be called with exactly two arguments as inputs, miles and feet.

Alternatively, you can give optional default values for the arguments. In this case, if the function is called with fewer arguments, the default values are used to fill in the rest.

[4]:
def to_kilometers_b(miles, feet=0):
    distance = miles + feet/5280
    return 1.60934 * distance

to_kilometers_b(100)
[4]:
160.934

If there are multiple optional arguments, you can call the function giving specific values for only some of the optional arguments, and letting Python fill in the defaults for the others.

[5]:
def to_kilometers_c(miles, feet=0, yards=0):
    distance = miles + feet/5280 + yards/1760
    return 1.60934 * distance
[6]:
to_kilometers_c(26, yards=385)
[6]:
42.194883125
[7]:
to_kilometers_c(26, feet=1155)
[7]:
42.194883125

It’s also valid to explicitly name arguments that don’t have default values, and this allows you to reorder arguments arbitrarily, although it’s generally not good coding practice to do so.

[8]:
to_kilometers_c(feet=1155, miles=26)
[8]:
42.194883125

If you don’t specify values for arguments that don’t have default values, you’ll get an error.

[9]:
to_kilometers_c(feet=100, yards=0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-fb71989af844> in <module>
----> 1 to_kilometers_c(feet=100, yards=0)

TypeError: to_kilometers_c() missing 1 required positional argument: 'miles'

Arbitrary Arguments

Functions that take an arbitrary number of arguments can do so using “star” notation.

[10]:
def county_names(*names):
    for name in names:
        print(name, "County")
[11]:
county_names("Pinellas", "Leon", "Monroe")
Pinellas County
Leon County
Monroe County

The argument name preceded by the star is used within the function as a tuple, containing all of the otherwise unnamed positional arguments passed to the function.

Other named arguments can appear after the “star” argument. In this case, you cannot set values for these arguments unless you explicitly give the argument name as a keyword argument.

[12]:
def highway_names(*names, heading=None):
    if heading:
        print(heading)
    for name in names:
        print("-",name)

highway_names("Alligator Alley", "Magic Carpet", heading="Unofficial Names")
Unofficial Names
- Alligator Alley
- Magic Carpet

Arbitrary keyword arguments can also be accepted using a double-star. In this case, the double-starred argument is used within the function as a dictionary, mapping the given keyword argument names to their values.

[13]:
def more_highway_names(**names):
    for key, value in names.items():
        print(key,"is",value)

more_highway_names(I_75="Alligator Alley", US_1="Magic Carpet")
I_75 is Alligator Alley
US_1 is Magic Carpet

Lambda Functions

Alternatively, sometimes simple functions are defined with the lambda keyword. In this case, the arguments are given before the colon, and a single expression is the return value given after the colon:

[14]:
lambda km: km / 1.60934
[14]:
<function __main__.<lambda>(km)>

Once defined, a function is a function, and it generally doesn’t matter if it was defined using lambda or def. In most python contexts, the lambda is only used when the function will not be named, but instead simply used one-time as an argument to some other command.

[15]:
list(map(lambda km: km / 1.60934, [5, 10, 42.194883125]))
[15]:
[3.106863683249034, 6.213727366498068, 26.218749999999996]

If you find “lambda” functions to be confusing or hard to read, you can take solace that you’re not alone. They are generally not actually required, and you can nearly always achieve the same effect using a regular named function.