Python functions - notes and study materials PDF

Title Python functions - notes and study materials
Course python programming
Institution Galgotias University
Pages 20
File Size 504.2 KB
File Type PDF
Total Downloads 108
Total Views 134

Summary

notes and study materials...


Description

Python Functions In Python, function is a group of related statements that perform a specific task. Functions help break our program into smaller and modular chunks. As our program grows larger and larger, functions make it more organized and manageable. Furthermore, it avoids repetition and makes code reusable.

Syntax of Function def function_name(parameters): """docstring""" statement(s)

Above shown is a function definition which consists of following components. 1. Keyword def marks the start of function header. 2. A function name to uniquely identify it. Function naming follows the same rules of writing identifiers in Python. 3. Parameters (arguments) through which we pass values to a function. They are optional. 4. A colon (:) to mark the end of function header. 5. Optional documentation string (docstring) to describe what the function does. 6. One or more valid python statements that make up the function body. Statements must have same indentation level (usually 4 spaces). 7. An optional return statement to return a value from the function.

def greet(name): """This function greets to the person passed in as parameter""" print("Hello, " + name + ". Good morning!")

Function Call

Once we have defined a function, we can call it from another function, program or even the Python prompt. To call a function we simply type the function name with appropriate parameters.

>>> greet('Paul') Hello, Paul. Good morning!

Note: Try running the above code into the Python shell to see the output.

Docstring The first string after the function header is called the docstring and is short for documentation string. It is used to explain in brief, what a function does. Although optional, documentation is a good programming practice. Unless you can remember what you had for dinner last week, always document your code. In the above example, we have a docstring immediately below the function header. We generally use triple quotes so that docstring can extend up to multiple lines. This string is available to us as __doc__ attribute of the function. For example: Try running the following into the Python shell to see the output. >>> print(greet.__doc__) This function greets to the person passed into the name parameter

The return statement The return statement is used to exit a function and go back to the place from where it was called.

Syntax of return return [expression_list]

This statement can contain expression which gets evaluated and the value is returned. If there is no expression in the statement or the return statement itself is not present inside a function, then the function will return the None object. For example: >>> print(greet("May")) Hello, May. Good morning! None Here, None is the returned value.

How Function works in Python?

def absolute_value(num): """This function returns the absolute value of the entered number""" if num >= 0: return num else: return -num

# Output: 2 print(absolute_value(2))

# Output: 4 print(absolute_value(-4))

Scope and Lifetime of variables Scope of a variable is the portion of a program where the variable is recognized. Parameters and variables defined inside a function is not visible from outside. Hence, they have a local scope. Lifetime of a variable is the period throughout which the variable exits in the memory. The lifetime of variables inside a function is as long as the function executes. They are destroyed once we return from the function. Hence, a function does not remember the value of a variable from its previous calls. Here is an example to illustrate the scope of a variable inside a function. def my_func(): x = 10 print("Value inside function:",x) x = 20 my_func() print("Value outside function:",x) Value inside function: 10 Value outside function: 20

Types of Functions Basically, we can divide functions into the following two types: 1. Built-in functions - Functions that are built into Python. 2. User-defined functions - Functions defined by the users themselves.

Python Function Arguments

def greet(name,msg): """This function greets to the person with the provided message""" print("Hello",name + ', ' + msg) greet("Monica","Good morning!")

If we call it with different number of arguments, the interpreter will complain. Below is a call to this function with one and no arguments along with their respective error messages.

>>> greet("Monica")

# only one argument

TypeError: greet() missing 1 required positional argument: 'msg' >>> greet()

# no arguments

TypeError: greet() missing 2 required positional arguments: 'name' and 'msg'

Variable Function Arguments Up until now functions had fixed number of arguments. In Python there are other ways to define a function which can take variable number of arguments. Three different forms of this type are described below.

Python Default Arguments Function arguments can have default values in Python. We can provide a default value to an argument by using the assignment operator (=). Here is an example. def greet(name, msg = "Good morning!"): """ This function greets to the person with the provided message. If message is not provided, it defaults to "Good morning!"

""" print("Hello",name + ', ' + msg) greet("Kate") greet("Bruce","How do you do?")

In this function, the parameter name does not have a default value and is required (mandatory) during a call. On the other hand, the parameter msg has a default value of "Good morning!". So, it is optional during a call. If a value is provided, it will overwrite the default value. Any number of arguments in a function can have a default value. But once we have a default argument, all the arguments to its right must also have default values. This means to say, non-default arguments cannot follow default arguments. For example, if we had defined the function header above as:

def greet(msg = "Good morning!", name):

We would get an error as:

SyntaxError: non-default argument follows default argument

Python Keyword Arguments When we call a function with some values, these values get assigned to the arguments according to their position. For example, in the above function greet(), when we called it as greet("Bruce","How do you do?"), the value "Bruce" gets assigned to the argument name and similarly "How do you do?" to msg. Python allows functions to be called using keyword arguments. When we call functions in this way, the order (position) of the arguments can be changed. Following calls to the above function are all valid and produce the same result.

>>> # 2 keyword arguments

>>> greet(name = "Bruce",msg = "How do you do?")

>>> # 2 keyword arguments (out of order) >>> greet(msg = "How do you do?",name = "Bruce")

>>> # 1 positional, 1 keyword argument >>> greet("Bruce",msg = "How do you do?")

As we can see, we can mix positional arguments with keyword arguments during a function call. But we must keep in mind that keyword arguments must follow positional arguments. Having a positional argument after keyword arguments will result into errors. For example the function call as follows:

greet(name="Bruce","How do you do?")

Will result into error as:

SyntaxError: non-keyword arg after keyword arg

Python Arbitrary Arguments Sometimes, we do not know in advance the number of arguments that will be passed into a function.Python allows us to handle this kind of situation through function calls with arbitrary number of arguments. In the function definition we use an asterisk (*) before the parameter name to denote this kind of argument. Here is an example. def greet(*names): """This function greets all

the person in the names tuple.""" # names is a tuple with arguments for name in names: print("Hello",name) greet("Monica","Luke","Steve","John")

Output

Hello Monica Hello Luke Hello Steve Hello John

Here, we have called the function with multiple arguments. These arguments get wrapped up into a tuple before being passed into the function. Inside the function, we use a forloop to retrieve all the arguments back.

Python Recursion Python Recursive Function We know that in Python, a function can call other functions. It is even possible for the function to call itself. These type of construct are termed as recursive functions. Following is an example of recursive function to find the factorial of an integer. Factorial of a number is the product of all the integers from 1 to that number. For example, the factorial of 6 (denoted as 6!) is 1*2*3*4*5*6 = 720.

Example of recursive function # An example of a recursive function to # find the factorial of a number def calc_factorial(x): """This is a recursive function to find the factorial of an integer"""

if x == 1: return 1 else: return (x * calc_factorial(x-1)) num = 4 print("The factorial of", num, "is", calc_factorial(num))

Advantages of recursion 1. Recursive functions make the code look clean and elegant. 2. A complex task can be broken down into simpler sub-problems using recursion. 3. Sequence generation is easier with recursion than using some nested iteration.

Disadvantages of recursion 1. Sometimes the logic behind recursion is hard to follow through. 2. Recursive calls are expensive (inefficient) as they take up a lot of memory and time. 3. Recursive functions are hard to debug.

Functions are known under various names in programming languages, e.g. as subroutines, routines, procedures, methods, or subprograms. A function in Python is defined by a def statement. The general syntax looks like this: def function-name(Parameter list): statements, i.e. the function body The parameter list consists of none or more parameters. Parameters are called arguments, if the function is called. The function body consists of indented statements. The function body gets executed every time the function is called. Function bodies can contain one or more return statement. They can be situated anywhere in the function body. A return statement ends the execution of the function call and "returns" the result, i.e. the value of the expression following the return keyword, to the caller. If the return statement is without an expression, the special value None is returned. If there is no return statement in the function code, the function ends, when the control flow reaches the end of the function body and the value "None" will be returned.

def fahrenheit(T_in_celsius): """ returns the temperature in degrees Fahrenheit """ return (T_in_celsius * 9 / 5) + 32

for t in (22.6, 25.8, 27.3, 29.8): print(t, ": ", fahrenheit(t)) The output of this script looks like this: 22.6 :

72.68

25.8 :

78.44

27.3 :

81.14

29.8 :

85.64

Optional Parameters

Functions can have optional parameters, also called default parameters. Default parameters are parameters, which don't have to be given, if the function is called. In this case, the default values are used. We will demonstrate the operating principle of default parameters with an example. The following little script, which isn't very useful, greets a person. If no name is given, it will greet everybody: def Hello(name="everybody"): """ Greets a person """ print("Hello " + name + "!")

Hello("Peter") Hello() The output looks like this: Hello Peter! Hello everybody! Docstring The first statement in the body of a function is usually a string, which can be accessed with function_name.__doc__ This statement is called Docstring. Example: def Hello(name="everybody"): """ Greets a person """ print("Hello " + name + "!")

print("The docstring of the function Hello: " + Hello.__doc__) The output: The docstring of the function Hello:

Greets a person

Keyword Parameters Using keyword parameters is an alternative way to make function calls. The definition of the function doesn't change. An example:

def sumsub(a, b, c=0, d=0): return a - b + c - d

print(sumsub(12,4)) print(sumsub(42,15,d=10)) Keyword parameters can only be those, which are not used as positional arguments. We can see the benefit in the example. If we hadn't keyword parameters, the second call to function would have needed all four arguments, even though the c needs just the default value: print(sumsub(42,15,0,10)) Return Values In our previous examples, we used a return statement in the function sumsub but not in Hello. So, we can see that it is not mandatory to have a return statement. But what will be returned, if we don't explicitly give a return statement. Let's see: def no_return(x,y): c = x + y

res = no_return(4,5) print(res) If we start this little script, None will be printed, i.e. the special value None will be returned by a return-less function. None will also be returned, if we have just a return in a function without an expression: def empty_return(x,y): c = x + y return

res = empty_return(4,5) print(res) Otherwise the value of the expression following return will be returned. In the next example 9 will be printed: def return_sum(x,y): c = x + y return c

res = return_sum(4,5) print(res)

Returning Multiple Values A function can return exactly one value, or we should better say one object. An object can be a numerical value, like an integer or a float. But it can also be e.g. a list or a dictionary. So, if we have to return for example 3 integer values, we can return a list or a tuple with these three integer values. This means that we can indirectly return multiple values. The following example, which is calculating the Fibonacci boundary for a positive number, returns a 2-tuple. The first element is the Largest Fibonacci Number smaller than x and the second component is the Smallest Fibonacci Number larger than x. The return value is immediately stored via unpacking into the variables lub and sup: def fib_intervall(x): """ returns the largest fibonacci number smaller than x and the lowest fibonacci number higher than x""" if x < 0: return -1 (old,new, lub) = (0,1,0) while True: if new < x: lub = new (old,new) = (new,old+new) else: return (lub, new)

while True: x = int(input("Your number: ")) if x >> def f(**kwargs): ...

print(kwargs)

... >>> f() {} >>> f(de="German",en="English",fr="French") {'fr': 'French', 'de': 'German', 'en': 'English'} >>>

One use case is the following: >>> def f(a,b,x,y): ...

print(a,b,x,y)

… >>> d = {'a':'append', 'b':'block','x':'extract','y':'yes'} >>> f(**d) ('append', 'block', 'extract', 'yes') Recursive Functions in Python Now we come to implement the factorial in Python. It's as easy and elegant as the mathematical definition. def factorial(n): if n == 1: return 1 else: return n * factorial(n-1) We can track how the function works by adding two print() function to the previous function definition: def factorial(n): print("factorial has been called with n = " + str(n)) if n == 1:

return 1 else: res = n * factorial(n-1) print("intermediate result for ", n, " * factorial(" ,n-1, "): ",res) return res

print(factorial(5)) This Python script outputs the following results: factorial has been called with n = 5 factorial has been called with n = 4 factorial has been called with n = 3 factorial has been called with n = 2 factorial has been called with n = 1 intermediate result for

2

* factorial( 1 ):

2

intermediate result for

3

* factorial( 2 ):

6

intermediate result for

4

* factorial( 3 ):

24

intermediate result for

5

* factorial( 4 ):

120

120 Let's have a look at an iterative version of the factorial function. def iterative_factorial(n): result = 1 for i in range(2,n+1): result *= i return result The Fibonacci numbers are the numbers of the following sequence of integer values: 0,1,1,2,3,5,8,13,21,34,55,89, ... The Fibonacci numbers are defined by: Fn = Fn-1 + Fn-2 with F0 = 0 and F1 = 1

The Fibonacci numbers are the result of an artificial rabbit population, satisfying the following conditions:

 

 

a newly born pair of rabbits, one male, one female, build the initial population these rabbits are able to mate at the age of one month so that at the end of its second month a female can bring forth another pair of rabbits these rabbits are immortal a mating pair always produces one new pair (one male, one female) every month from the second month onwards

The Fibonacci numbers are the numbers of rabbit pairs after n months, i.e. after 10 months we will have F10 rabits. The Fibonacci numbers are easy to write as a Python function. It's more or less a one to one mapping from the mathematical definition: def fib(n): if n == 0: return 0 elif n == 1: return 1 else: return fib(n-1) + fib(n-2) An iterative solution is also easy to write, though the recursive solution looks more like the definition: def fibi(n): old, new = 0, 1 if n == 0: return 0 for i in range(n-1): old, new = new, old + new return new What's wrong with our recursive implementation? Let's have a look at the calculation tree, i.e. the order in which the functions are called. fib() is substituted by f().

We can see that the subtree f(2) appears 3 times and the subtree for the calculation of f(3) two times. If you imagine extending this tree for f(6), you will understand that f(4) will be called two times, f(3) three times and so on. This means, our recursion doesn't remember previously calculated values. We can implement a "memory" for our recursive version by using a dictionary to save the previously calculated values.

memo = {0:0, 1:1} def fibm(n): if not n in memo: memo[n] = fibm(n-1) + fibm(n-2) return memo[n]...


Similar Free PDFs