Tuesday, 11 August 2020

Functions



   
     In this blog you’ll learn to write functions, which are named blocks of code that are designed to do one specific job. When you want to perform a particular task that you’ve defined in a function, you call the name of the function responsible for it. If you need to perform that task multiple times throughout your program, you don’t need to type all the code for the same task again and again; you just call the function dedicated to handling that task, and the call tells Python to run the code inside the function. You’ll find that using functions makes your programs easier to write, read, test, and fix.
    In this blog you’ll also learn ways to pass information to functions. You’ll learn how to write certain functions whose primary job is to display information and other functions designed to process data and return a value or set of values. 

Defining a Function

Here’s a simple function named greet_user() that prints a greeting:

def greet_user(): 
    """Display a simple greeting.""" 
    print("Hello!") 

greet_user()

The first line uses the keyword def to inform Python that you’re defining a function. Any indented lines that follow def greet_user(): make up the body of the function. When you want to use this function, you call it. A function call tells Python to execute the code in the function. To call a function, you write the name of the function, followed by any necessary information in parentheses.

Passing Information to a Function

Modified slightly, the function greet_user() can not only tell the user Hello! but also greet them by name. For the function to do this, you enter username in the parentheses of the function’s definition at def greet_user(). By adding username here you allow the function to accept any value of username you specify. The function now expects you to provide a value for username each time you call it. When you call greet_user(), you can pass it a name, such as 'prasanna', inside the parentheses:

def greet_user(username): 
    """Display a simple greeting.""" 
    print("Hello, " + username.title() + "!") 
greet_user('prasanna')

Output
Hello, Prasanna! 

Entering greet_user('prasanna') calls greet_user() and gives the function the information it needs to execute the print statement. The function accepts the name you passed it and displays the greeting for that name.

Arguments and Parameters

The variable username in the definition of greet_user() is an example of a parameter, a piece of information the function needs to do its job. The value 'prasanna' in greet_user('prasanna') is an example of an argument. An argument is a piece of information that is passed from a function call to a function. When we call the function, we place the value we want the function to work with in parentheses.

Passing Arguments

Because a function definition can have multiple parameters, a function call may need multiple arguments. You can pass arguments to your functions in a number of ways. You can use positional arguments, which need to be in the same order the parameters were written; keyword arguments, where each argument consists of a variable name and a value; and lists and dictionaries of values. Let’s look at each of these in turn.

Positional Arguments

When you call a function, Python must match each argument in the function call with a parameter in the function definition. The simplest way to do this is based on the order of the arguments provided. Values matched up this way are called positional arguments.
To see how this works, consider a function that displays information about pets. The function tells us what kind of animal each pet is and the pet’s name, as shown here:

def describe_pet(animal_type, pet_name): 
    """Display information about a pet.""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 

describe_pet('parrot', 'coco')

Output
I have a parrot. 
My parrot's name is Coco.

Multiple Function Calls

You can call a function as many times as needed. Describing a second, different pet requires just one more call to describe_pet():

def describe_pet(animal_type, pet_name): 
    """Display information about a pet.""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 

describe_pet('parrot', 'coco')
describe_pet('dog', 'Maski')

Output
I have a parrot. 
My parrot's name is Coco.

I have a dog. 
My dog's name is Maski.

You can use as many positional arguments as you need in your functions. Python works through the arguments you provide when calling the function and matches each one with the corresponding parameter in the function’s definition.

Order Matters in Positional Arguments 

You can get unexpected results if you mix up the order of the arguments in a function call when using positional arguments:

def describe_pet(animal_type, pet_name): 
    """Display information about a pet.""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 

describe_pet('coco', 'parrot')

Output
I have a coco. 
My parrot's name is Parrot.

If you get funny results like this, check to make sure the order of the arguments in your function call matches the order of the parameters in the function’s definition.

Keyword Arguments

A keyword argument is a name-value pair that you pass to a function. You directly associate the name and the value within the argument, so when you pass the argument to the function, there’s no confusion.
 Keyword arguments free you from having to worry about correctly ordering your arguments in the function call, and they clarify the role of each value in the function call.

def describe_pet(animal_type, pet_name): 
    """Display information about a pet.""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 

describe_pet(animal_type='parrot', pet_name='coco') 
describe_pet(pet_name='coco', animal_type='parrot'

Output
I have a coco. 
My parrot's name is Parrot.
I have a coco. 
My parrot's name is Parrot.

Default Values

When writing a function, you can define a default value for each parameter. If an argument for a parameter is provided in the function call, Python uses the argument value. If not, it uses the parameter’s default value.

def describe_pet(pet_name, animal_type='dog'):
    """Display information about a pet.""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 

describe_pet(pet_name='willie')

Output
I have a dog. 
My dog's name is Willie. 

Return Values

A function doesn’t always have to display its output directly. Instead, it can process some data and then return a value or set of values. The value the function returns is called a return value. The return statement takes a value from inside a function and sends it back to the line that called the function. Return values allow you to move much of your program’s grunt work into functions, which can simplify the body of your program.

Returning a Simple Value

Let’s look at a function that takes a first and last name, and returns a neatly formatted full name:

def get_formatted_name(first_name, last_name): 
    """Return a full name, neatly formatted.""" 
    full_name = first_name + ' ' + last_name 
    return full_name.title() 

musician = get_formatted_name('Luke', 'Bryan') 
print(musician)

Output
Luke Bryan

When you call a function that returns a value, you need to provide a variable where the return value can be stored. In this case, the returned value is stored in the variable musician. The output shows a neatly formatted name made up of the parts of a person’s name.

Returning a Dictionary

A function can return any kind of value you need it to, including more complicated data structures like lists and dictionaries. For example, the following function takes in parts of a name and returns a dictionary representing a person:

def build_person(first_name, last_name): 
    """Return a dictionary of information about a person.""" 
    person = {'first': first_name, 'last': last_name} 
    return person 

musician = build_person('jimi', 'hendrix') 
print(musician) 

Output
{'first': 'jimi', 'last': 'hendrix'}

This function takes in simple textual information and puts it into a more meaningful data structure that lets you work with the information beyond just printing it.

Passing a List

You’ll often find it useful to pass a list to a function, whether it’s a list of names, numbers, or more complex objects, such as dictionaries. When you pass a list to a function, the function gets direct access to the contents of the list. Let’s use functions to make working with lists more efficient.

def greet_users(names): 
    """Print a simple greeting to each user in the list.""" 
    for name in names: 
        msg = "Hello, " + name.title() + "!" 
        print(msg) 

usernames = ['hannah', 'ty', 'margot'] 
greet_users(usernames)

Output
Hello, Hannah! 
Hello, Ty! 
Hello, Margot! 

Modifying a List in a Function

When you pass a list to a function, the function can modify the list. Any changes made to the list inside the function’s body are permanent, allowing you to work efficiently even when you’re dealing with large amounts of data. Consider a company that creates 3D printed models of designs that users submit. Designs that need to be printed are stored in a list, and after being printed they’re moved to a separate list. The following code does this:

def print_models(unprinted_designs, completed_models): 
 """ 
Simulate printing each design, until none are left. Move each design to completed_models after printing. 
""" 
 while unprinted_designs: 
     current_design = unprinted_designs.pop() 

     # Simulate creating a 3D print from the design.  
    print("Printing model: " + current_design) 
    completed_models.append(current_design)

def show_completed_models(completed_models): 
    """Show all the models that were printed.""" 
     print("\nThe following models have been printed:")  
    for completed_model in completed_models: 
         print(completed_model) 

unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron'] 
completed_models = []

print_models(unprinted_designs, completed_models) 
show_completed_models(completed_models)

Output
Printing model: dodecahedron 
Printing model: robot pendant 
Printing model: iphone case 

The following models have been printed: 
dodecahedron 
robot pendant 
iphone case

Preventing a Function from Modifying a List 

Sometimes you’ll want to prevent a function from modifying a list. In such case, you can send a copy of a list to a function like this:

print_models(unprinted_designs[:], completed_models)

The slice notation [:] makes a copy of the list to send to the function. The function print_models() can do its work because it still receives the names of all unprinted designs. But this time it uses a copy of the original unprinted designs list, not the actual unprinted_designs list. The list completed_models will fill up with the names of printed models like it did before, but the original list of unprinted designs will be unaffected by the function. 

Passing an Arbitrary Number of Argument

Sometimes you won’t know ahead of time how many arguments a function needs to accept. Fortunately, Python allows a function to collect an arbitrary number of arguments from the calling statement.
For example, consider a function that builds a pizza. It needs to accept a number of toppings, but you can’t know ahead of time how many toppings a person will want. The function in the following example has one parameter, *toppings, but this parameter collects as many arguments as the calling line provides: 

def make_pizza(*toppings): 
    """Print the list of toppings that have been requested.""" 
    print(toppings) 

make_pizza('pepperoni') 
make_pizza('mushrooms', 'green peppers', 'extra cheese') 

Output
('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')

Mixing Positional and Arbitrary Arguments

If you want a function to accept several different kinds of arguments, the parameter that accepts an arbitrary number of arguments must be placed last in the function definition. Python matches positional and keyword arguments first and then collects any remaining arguments in the final parameter.
rameter. For example, if the function needs to take in a size for the pizza, that parameter must come before the parameter *toppings:

def make_pizza(size, *toppings): 
    """Summarize the pizza we are about to make.""" 
    print("\nMaking a " + str(size) + "-inch pizza with the following toppings:") 
    for topping in toppings: 
        print("- " + topping) 

make_pizza(16, 'pepperoni') 
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

In the function definition, Python stores the first value it receives in the parameter size. All other values that come after are stored in the tuple toppings. The function calls include an argument for the size first, followed by as many toppings as needed.

Output
Making a 16-inch pizza with the following toppings: 
- pepperoni 

Making a 12-inch pizza with the following toppings: 
- mushrooms 
- green peppers 
- extra cheese

Using Arbitrary Keyword Arguments

Sometimes you’ll want to accept an arbitrary number of arguments, but you won’t know ahead of time what kind of information will be passed to the function. In this case, you can write functions that accept as many key-value pairs as the calling statement provides. One example involves building user profiles: you know you’ll get information about a user, but you’re not sure what kind of information you’ll receive. The function build_profile() in the following example always takes in a first and last name, but it accepts an arbitrary number of keyword arguments as well: 

def build_profile(first, last, **user_info): 
    """Build a dictionary containing everything we know about a user.""" 
    profile = {} u 
    profile['first_name'] = first profile['last_name'] = last v 
    for key, value in user_info.items(): 
        profile[key] = value 
    return profile 

user_profile = build_profile('albert', 'einstein', location='princeton', field='physics') 
print(user_profile)

Output
{'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'field': 'physics'}

The definition of build_profile() expects a first and last name, and then it allows the user to pass in as many name-value pairs as they want. The double asterisks before the parameter **user_info cause Python to create an empty dictionary called user_info and pack whatever name-value pairs it receives into this dictionary. Within the function, you can access the namevalue pairs in user_info just as you would for any dictionary.

You can mix positional, keyword, and arbitrary values in many different ways when writing your own functions. It’s useful to know that all these argument types exist because you’ll see them often when you start reading other people’s code. It takes practice to learn to use the different types correctly and to know when to use each type. For now, remember to use the simplest approach that gets the job done. As you progress you’ll learn to use the most efficient approach each time.

Storing Your Functions in Modules

One advantage of functions is the way they separate blocks of code from your main program. By using descriptive names for your functions, your main program will be much easier to follow. You can go a step further by storing your functions in a separate file called a module and then importing that module into your main program. An import statement tells Python to make the code in a module available in the currently running program file. 
Storing your functions in a separate file allows you to hide the details of your program’s code and focus on its higher-level logic. It also allows you to reuse functions in many different programs. When you store your functions in separate files, you can share those files with other programmers without having to share your entire program. Knowing how to import functions also allows you to use libraries of functions that other programmers have written. 
There are several ways to import a module, and I’ll show you each of these briefly.

Importing an Entire Module

To start importing functions, we first need to create a module. A module is a file ending in .py that contains the code you want to import into your program. While importing you simply write import followed by the name of the module, makes every function from the module available in your program. If you use this kind of import statement to import an entire module named module_name.py, each function in the module is available through the following syntax:
module_name.function_name()

In file pizza.py,

def make_pizza(size, *toppings): 
    """Summarize the pizza we are about to make.""" 
    print("\nMaking a " + str(size) + "-inch pizza with the following toppings:") 
    for topping in toppings: 
        print("- " + topping)

In file making_pizzas.py,

import pizza
pizza.make_pizza(16, 'pepperoni') 
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

When we run making_pizzas.py,
Output
Making a 16-inch pizza with the following toppings: 
- pepperoni 

Making a 12-inch pizza with the following toppings: 
- mushrooms 
- green peppers 
- extra cheese

Importing Specific Functions

You can also import a specific function from a module. Here’s the general syntax for this approach:
from module_name import function_name

You can import as many functions as you want from a module by separating each function’s name with a comma:
from module_name import function_0, function_1, function_2

The making_pizzas.py example would look like this if we want to import just the function we’re going to use:
from pizza import make_pizza 
make_pizza(16, 'pepperoni') 
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

Using as to Give a Function an Alias

If the name of a function you’re importing might conflict with an existing name in your program or if the function name is long, you can use a short, unique alias—an alternate name similar to a nickname for the function. You’ll give the function this special nickname when you import the function.
Here we give the function make_pizza() an alias, mp(), by importing make_pizza as mp. The as keyword renames a function using the alias you provide:

from pizza import make_pizza as mp 
mp(16, 'pepperoni') 
mp(12, 'mushrooms', 'green peppers', 'extra cheese') 

Using as to Give a Module an Alias

You can also provide an alias for a module name. Giving a module a short alias, like p for pizza, allows you to call the module’s functions more quickly. Calling p.make_pizza() is more concise than calling pizza.make_pizza():

import pizza as p 
p.make_pizza(16, 'pepperoni') 
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

Importing All Functions in a Module

You can tell Python to import every function in a module by using the asterisk (*) operator:

from pizza import * 
make_pizza(16, 'pepperoni') 
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

Conclusion

In this blog you learned how to write functions and to pass arguments so that your functions have access to the information they need to do their work. You learned how to use positional and keyword arguments, and how to accept an arbitrary number of arguments. You saw functions that display output and functions that return values. You learned how to use functions with lists, dictionaries, if statements, and while loops. You also saw how to store your functions in separate files called modules, so your program files will be simpler and easier to understand.

One of your goals as a programmer should be to write simple code that does what you want it to, and functions help you do this. They allow you to write blocks of code and leave them alone once you know they work. When you know a function does its job correctly, you can trust that it will continue to work and move on to your next coding task.

Functions allow you to write code once and then reuse that code as many times as you want. When you need to run the code in a function, all you need to do is write a one-line call and the function does its job. When you need to modify a function’s behavior, you only have to modify one block of code, and your change takes effect everywhere you’ve made a call to that function.

Using functions makes your programs easier to read, and good function names summarize what each part of a program does. Reading a series of function calls gives you a much quicker sense of what a program does than reading a long series of code blocks.

Functions also make your code easier to test and debug. When the bulk of your program’s work is done by a set of functions, each of which has a specific job, it’s much easier to test and maintain the code you’ve written. You can write a separate program that calls each function and tests whether each function works in all the situations it may encounter. When you do this, you can be confident that your functions will work properly each time you call them.


In next blog you’ll learn to write classes. Classes combine functions and data into one neat package that can be used in flexible and efficient ways.

Post a Comment

Let's make it better!
Comment your thoughts...

Whatsapp Button works on Mobile Device only

Start typing and press Enter to search