# Function In Python

## 1. DEFINING PYTHON’s FUNCTION

Let’s first write a very simple Python `function` and then use it as an example to define the elements of a `function`

```python
# defining the function
def display_message():
	# docstring to define the purpose of function
    """ fucntion will print a welcome message with todays learning theme """
    print("Hello everyone, in this section, we will learn about Python's functions.")

# calling the function
display_message()
```

```
Hello everyone, in this section, we will learn about Python's functions.
```

#### a. Keyword Definition

```python
def display_message():
```

* to define the function, we will use the keyword `def` followed by the name of the function `display_message`
* if the function do need some information to do its job, this piece of information is provided within the parenthesis `( )`
* the function ends with a colon `:` sign

#### b. Body of the function

```python
""" function will print a welcome message with todays learning theme """
    print("Hello everyone, in this section, we will learn about Python's functions.")
```

* all indented lines follow the colon sign `:` represents the *body of the function*
* text within the triple commas is called **docstring** and it give us the opportunity to tell the reader of code, what the function does. It is essentially the comments, Python uses it to generate documentation for the functions in your programs
* the `print()` statement runs any time we *call the function*

#### c. Calling a function

```python
display_message()
```

* to execute the function, we just need to write the name of function (and provide any information, within parenthesis, if function needs it) This call essentially tells Python to execute the code in the function

### 1.1. Passing Information (arguments) to a Function

Let’s move a step further and write a function that accepts some information to work inside the function. This is called **argument**

```python
# defining function with argument, title
def favorite_book(title):
	# docstring
    """ printing the favorite book """
    print(f"One of my favorite book is {title.title()}.")

favorite_book('harry potter')
```

```
One of my favorite book is Harry Potter.
```

#### a. Parameter

* a **parameter** is a piece of information the function needs to do its job
* the variable `title` in the definition of function `favorite_book` is an example of parameter

#### b. Argument

* an **argument** is a piece of information that is passed from a function call to a function
* The value `harry_potter` in `favorite_book(title)` is an example of an argument

## 2. PASSING ARGUMENTS

In the above example, the *function definition* only include one parameter and therefore one argument is supplied in the *function call*. However, function can have multiple parameters and arguments. You can pass arguments to your functions in a number of ways:

* \*\* positional arguments\*\* — which need to be in the same order as the parameters;
* **keyword arguments** — where each argument has a variable name and a value, that is, name-value pair;
* and **lists and dictionaries** of values

### 2.1. Positional Arguments

> When we call a function, Python match each ***argument in the function call*** with each ***parameter in the function definition***. Therefore the values matched up this way (based on the order of the arguments ) are called **positional arguments**. Let’s write a function with positional arguments:

```python
# defining function with two parameters, named, size and message
def make_shirt(size, message):
    # docstring
    """To print the size and message on the shirt """
    # print statement
    print(f"You have ordered the shirt in size {size.upper()}\nThe following message will be printed on the shirt: {message}")

# call the function
# positional arguments
make_shirt("xl", "I code in Python")
```

```
You have ordered the shirt in size XL
The following message will be printed on the shirt: I code in Python
```

* we defined the `function` named `make_shirt` that requires two *parameters* — `size` and `message`
* we then write the *body of function* with a `print` statement using the given values of *parameters*
* lastly, we *call a function* by providing the *arguments* — `xl` and `I code in Python`

#### Important Points

* **Multiple function call:** You can call the function as many times as you want with new set of arguments. This makes the function very powerful — write code once and use it several times
* **Order matters in positional arguments:** Make sure to provide the arguments in same order as the parameters in function definition

### 2.2. Keyword Arguments

> A keyword argument is essentially **name-value pair** that we pass to a function, where `name` is the parameter name . Therefore, as we explicitly provides the name of parameter in the argument, the order of arguments doesn’t matter. We will rewrite the above example using keyword arguments:

```python
# defining function with two parameters, named, size and message
def make_shirt(size, message):
    # docstring
    """To print the size and message on the shirt """
    # print statement
    print(f"You have ordered the shirt in size {size.upper()}\nThe following message will be printed on the shirt: {message}")

# call the function
# keyword arguments
make_shirt(message = "I code in Python.", size ="xl")
```

```
You have ordered the shirt in size XL
The following message will be printed on the shirt: I code in Python.
```

As you can see that when we *call the function*, we used the *name-value pair*, where name is the name of parameter in the function definition. In this case, the order of arguments doesn’t matter.

```python
make_shirt(message = "I code in Python.", size ="xl")
```

### 2.3. Default Values

> When writing a function, we can define \*\* default value\*\* for each parameter. In this case, if an argument for a parameter is provided in the function call, Python uses the argument value, otherwise, it uses the parameter’s default value (as provided in function definition) In the following example, we will copy the code from `make_shirt` function and will provide the default value for parameter `message`

```python
# defining function with two parameters, named, size and message
# default value of paramter, message, is provided
def make_shirt(size, message = 'I love Python.'):
    # docstring
    """To print the size and message on the shirt """
     # print statement
    print(f"You have ordered the shirt in size {size.upper()}\nThe following message will be printed on the shirt: {message}")

# call the function
# using default value of parameter
make_shirt("large")
```

```
You have ordered the shirt in size LARGE
The following message will be printed on the shirt: I love Python.
```

> Note that the parameter with default value must be the last parameter(s) In this example, we will overwrite the default value of the parameter:

```
...python
make_shirt("large", "I love Java.")
```

## 3. RETURN VALUES

> A function doesn’t always have to display its output directly (like printing some message, as we have done so far). Instead, we can write a function that process some data and then return a *value or set of values*. The value the function returns is called a **return value**. This\*\* return value\*\* can be used anyway we want. **NOTE:** When you call a function that returns a value, you need to provide a variable where the return value can be stored.

### 3.1. Returning A Simple Value

Let’s write a simple function that returns the string:

```python
# defining function with two parameters, named, city and country
def city_country(city, country):
     # docstring
    """ combining the parameters and returning formatted_string"""
    # storing values of given parameter inside a variable
    formatted_string = f"{city.title()} , {country.title()}"
    # returning the variable value
    return formatted_string

# calling the function
# storing the returned value inside country_1
country_1 = city_country("london", "england")
print(country_1)
```

```
London , England
```

when we call the function `city_country` we stored its returned value in separate variable called `country_1` and then `print` that variable

### 3.2. Making an Argument Optional

> We can use empty default values for a parameter to make an argument optional. As a rule, all (empty) default value parameter(s) are moved to the end of the parameters list

Let’s write a function which stores a dictionary containing artist name and album title. However, we also would like to store an optional parameter — i.e, no of tracks in the album. Here is how we deal with this situation:

```python
# defining function with three parameters
# last parameter, tracks, is optional with empty default value
def make_album(artist_name, album_title, tracks = ''):
     # docstring
    """ making album """
	# making dictionary from the parameter values
    album_dic = {'artist_name': artist_name, 'album_title': album_title}
    # checking if the number of tracks are provided in argument
    if tracks:
        # if yes, add it to the dictionary
        tracks_integer = int(tracks)
        album_dic['tracks'] = tracks_integer
    # returning the album_dict dictionary
    return album_dic

# calling function 1 without 'tracks'
album_1 = make_album('usher', 'confessions')

# calling function 2 with 'tracks'
album_2 = make_album ('akon', 'konvicted', tracks ='13')

# printing albums made from returned values of the function
print(album_1)
print(album_2)
```

```
{'artist_name': 'usher', 'album_title': 'confessions'}
{'artist_name': 'akon', 'album_title': 'konvicted', 'tracks': 13}
```

* we have used the parameter `tracks` in the function definition as an optional argument
* in the body of function, we made a dictionary with required parameter values, then,
* we used `if` statement to check whether the user provided optional argument, `tracks`. If yes, we concatenated that value to the dictionary
* we used `return` statement to output the dictionary
* we call the function two times — one with `tracks` and one without it
* we then print the variables `album_1` and `album_2` that stored the function output value

### 3.3. Using a Function with a while Loop

> We will first write the function as we have done in above example, and then get the argument values once `input` statements

```python
# defining function with two required parameters
def make_album(artist_name, album_title):
     # docstring
    """ storing parameter values in dictionary and returning it"""
    # making dictionary from the parameter values
    album_dic = {'artist_name': artist_name, 'album_title': album_title}
    # returning the album_dict dictionary
    return album_dic

# while loop will run as long as it gets two input values
while True:
    a_name = input("Enter the artist name: ")
    alb_name = input("Enter the album name: ")
    break

# calling function 
album_info = make_album(a_name, alb_name)
# printing album made from returned value of the function
print(album_info)
```

```
Enter the artist name: usher
Enter the album name: confessions
{'artist_name': 'usher', 'album_title': 'confessions'}
```

* the above example only takes a pair of response
* both user `inputs` were mandatory and loop breaks after that
* we used the input values, stored inside the variables, `a_name` and `alb_name` to call the function
* lastly, we print the `album_info`

## 4. PASSING A LIST IN FUNCTION

Python’s functions are very flexible to be used with different data types. In the following example, we will use as a `list` parameter of a function:

```python
# defining function with list as a parameter
def show_magicians(magicians):
     # docstring
    """ a function to print to content of the list """
    # printing items of list, using for loop
    print("These are some great magicinans in Sweden:")
    for magician in magicians:
        print(magician.title())

# calling function by providing list as argument
magicians = ['abbott', 'costello', 'shawn', 'michael']
show_magicians(magicians)
```

```
These are some great magicinans in Sweden:
Abbott
Costello
Shawn
Michael
```

In the above example, we passed a single `list`. However, we are free to supply a series of `list` as argument

### 4.1. Modifying a List in a Function

In following example, we are going to use two lists in one function with purpose of moving the list items from one list to another (i.e, modifying the list)

```python
# defining function with two parameters
# both of them are Python list
def inventory(ordered_items, shipped_items):
    # docstring
    """ shifting items from one list(ordered) to another(shipped) """
    # while loop will run as long as ordered_item list is not empty
    while ordered_items:
        ordered_item = ordered_items.pop()
        print(f"Shipping: {ordered_item.title()}")
        shipped_items.append(ordered_item)

# defining the arguments (lists) and calling the function
ordered_items = ['apple', 'orange', 'strawberry']
shipped_items = []
inventory(ordered_items, shipped_items)
```

```
Shipping: Strawberry
Shipping: Orange
Shipping: Apple
```

* we used two lists, `ordered_items` and `shipped_items` as parameters in the definition of the function
* we used `while` loop which keep running until the list, `ordered_list` is empty
* we `pop` each item from `ordered_list` , store it in the variable `ordered_item` , print it and finally append it to the list `shipped_items`

### 4.2. Working with the Copy of a List

Let suppose, we want to keep the list `ordered_items` unmodified. Here is the trick in python on how to stop the list to be modified — when call the function, use the following syntax:

```python
function_name(list_name[:])
```

> The slice notation `[:]` makes a copy of the list.

```python
# defining the arguments (lists) and calling the function
# we will use [:] to work with copy of list
ordered_items = ['apple', 'orange', 'strawberry']
shipped_items = []
inventory(ordered_items[:], shipped_items)

print("\nHere is a check that old list is still unmodified:")
print(ordered_items)
```

```
Shipping: Strawberry
Shipping: Orange
Shipping: Apple

Here is a check that old list is still unmodified:
['apple', 'orange', 'strawberry']
```

## 5. PASSING AN ARBITRARY NUMBER OF ARGUMENTS

> Sometimes function need to process the unknown number of parameters. To pass these arbitrary number of arguments, we need to add an asterisk `*` before a parameter name in the function definition. This asterisk `*` in the parameter name, tells Python to make an empty **tuple** and load whatever values it receives into this tuple. NOTE: Python loads the arguments into a tuple, even if the function receives only one value

```python
# defining function that takes arbitrary no of parameters
def sandwich(*items):
    # docstring
    """Print the sandwich content, selected by the user """
    print("Your sandwich includes the following items:")
    for item in items:
        print("- " + item)

# calling function with two arguments
sandwich('egg', 'cheese')
# calling function with four arguments
sandwich('egg','beef','mushroom','cheese')
```

```
Your sandwich includes the following items:
- egg
- cheese
Your sandwich includes the following items:
- egg
- beef
- mushroom
- cheese
```

* we wrote the function to print all the items in a sandwich
* as we don’t know in advance that how many arguments does user supply, so we added an asterisk before a parameter name. In this way, our function can handle arbitrary number of arguments

### 5.1. Mixing Positional and Arbitrary Arguments

> If we want a function to accept *different kinds of arguments*, the parameter that accepts an arbitrary number of arguments must be placed last in the function definition.

> Python first matches the positional and keyword arguments and then process any remaining arguments in the final parameter. Let’s modify the `sandwich` function that takes the `size` of the sandwich as parameter but `items` parameter will be arbitrary:

```python
# defining function that takes one required and 
# one arbitrary parameter
def sandwich(size, *items):
     # docstring
    """Print the sandwich content, selected by the user """
    print("You have ordered a " + size + " sandwich.")
    # this section runs only if 'items' are provided
    if items:
        print("Your sandwich includes the following items:")
        for item in items:
            print("- " + item)
        
# calling function example 1
sandwich('12 inches','beef','mushroom','cheese')
# calling function example 2
sandwich('6 inches')
```

```
You have ordered a 12 inches sandwich.
Your sandwich includes the following items:
- beef
- mushroom
- cheese
You have ordered a 6 inches sandwich.
```

### 5.2. Using Arbitrary Keyword Arguments

> Until now, functions with arbitrary number of arguments, all of them are of one type (for example, sandwich *items* in the above example). However, if they are not of same type, we can use the name-value pair in the argument.

> The double asterisks `**` before the parameter tells Python to create an empty dictionary and load whatever name-value pairs it receives (in arguments) into this dictionary

```python
# defining function to build user profile
def build_profile(first,last,**user_info):
    # docstring
    """ build user profile and store its inside dict""" 
    user = {}
    user['first_name'] = first
    user['last_name'] = last
    for key,value in user_info.items():
        user[key] = value
    return user

# defining function to print the dictionary
def print_profile(profile_info):
    for key, value in profile_info.items():
        print(f"{key}: {value}")

# calling build_profile function
profile_dexter = build_profile('dexter', 'morgan',
age = 22,
location ='florida',
profession ='forensic analyst')

# calling print_profile function
# dictionary we just made by calling build_profile function
# is used as argument
print_profile(profile_dexter)
```

```
first_name: dexter
last_name: morgan
age: 22
location: florida
profession: forensic analyst
```

* we wrote two functions, first, `build_profile` to build the profile (in form of dictionary) using arbitrary number of (keyword) arguments and another, `print_profile` to print the key-value pair of the dictionary returned from the first function
* we first call the `build_profile` function and store its value (dictionary) inside a variable `profile_dexter`
* then we called the second function, `print_profile` by providing the variable `profile_dexter` as an argument

## 6. STORING YOUR FUNCTIONS IN MODULES

> We can go a step further by storing our functions in a separate file called a **module** and then importing that module into our main program using `import` statement.

> An `import` statement tells Python to make the code in a **module**(file) available in the currently running program file.

> A module is a file ending in `.py` that contains the code we want to import into our program

### 6.1. Importing an Entire Module

#### a. General syntax to import:

```python
import module_name
```

The above line `import module_name` tells Python to open the file `module_name.py` and copy all the functions from it into this program.

#### b. General syntax to call the function:

```python
module_name.function_name()
```

Enter the name of the module we imported, `module_name`, followed by the name of the function, `function_name()`, separated by a dot `.` **EXAMPLE:** Let suppose we store the function to build the profile in a separate file called `build_profile_module.py` then import the module and call this function:

```python
# importing moduele
import build_profile_module

# calling 'build_profile' function
person_1 = build_profile_module.build_profile('dexter', 'morgan',
age = 22,
location ='florida',
profession ='forensic analyst')

# calling 'print_profile' function
print_profile(person_1)
```

```
first_name: dexter
last_name: morgan
age: 22
location: florida
profession: forensic analyst
```

### 6.2. Importing Specific Functions

Rather than importing all the functions inside a file, we can import a specific function from a module.

#### a. General syntax to import

Importing a single function:

```python
from module_name import function_name
```

Importing series of function: We can import as many functions as we want from a module by separating each function’s name with a comma

```python
from module_name import function_0, function_1, function_2
```

#### b. General syntax to call the function:

```python
function_name()
```

As you can see, in this way, we don’t need to provide the name of module whenever we call a function

### 6.3. Importing All Functions in a Module

We can tell Python to `import` every function in a module by using the asterisk `*` operator. But you might be questioning that we can import all the function using the method described in 6.1, then we need this method? Using this (asterisk) way, we don’t need to use the module name anymore when calling the function

#### a. General syntax to import:

```python
from module_name import *
```

#### b. General syntax to call the function:

```python
function_name()
```

### 6.4. Give Function an Alias

A couple of reasons to use alias for a function name:

* name of a function we are importing might conflict with an existing name in our program
* function name is long, so we can use a short one

#### a. General syntax to import:

```python
from module_name import function_name as fn
```

#### b. General syntax to call the function:

```python
fn()
```

### 6.5. Give Module an Alias

In section 6.1, we used `import module_name` and then we need to use the module name every time we call the function (Remember the syntax?) Therefore to avoid using the long module name, we can import module using alias.

#### a. General syntax to import:

```python
import module_name as mn
```

#### b. General syntax to call the function:

```python
mn.function_name()
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://codingnotes.gitbook.io/coding_notes/coding/python/function-in-python.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
