Basics
Properties
- Python allows docstring for functions using triple-quotes(""" or ''').
- Python allows default arguments, but they should not be followed by non-default arguments.
- Python allows functions to return multiple values.
- Python allows functions to have variable length of arguments (using *args for non-keywords arguments and **kwargs for keyword arguments).
- Python allows anonymous functions.
- Python allows functions within functions.
Syntax and example
''' syntax '''
def function_name(parameters):
"""docstring"""
statement(s)
return expression
''' defining and calling a function '''
def fun():
print("Welcome to GFG")
fun()
Arguments
def print_double(x):
print(x*2)
print_double(10)
''' default arguments '''
def multiply(x, y=2):
print(x*y)
multiply(5, 10) # 50
multiply(5) # 10
''' keyword arguments '''
def multiply(multiplier1, multiplier2):
print(multiplier1 * multiplier2)
multiply(multiplier2=10, multiplier1=5)
''' variable length non-keywords argument using argv '''
def myFun(*argv):
for arg in argv:
print(arg)
myFun('Hello', 'Welcome', 'to', 'GeeksforGeeks')
''' variable length keywords argument using argv '''
def myFun(**kwargs):
for key, value in kwargs.items():
print("%s == %s" % (key, value))
myFun(first='Geeks', mid='for', last='Geeks')
''' args and kwargs '''
def myFun(*args,**kwargs):
print("args: ", args) # args: ('a', 'b', 'c')
print("kwargs: ", kwargs) # kwargs {'first': 'abc', 'mid': 'def', 'last': 'xyz'}
myFun('a','b','c',first="abc",mid="def",last="xyz")
Docstring
def evenOdd(x):
"""Function to check if the number is even or odd"""
if (x % 2 == 0):
print("even")
else:
print("odd")
print(evenOdd.__doc__) # Function to check if the number is even or odd
Anonymous functions
def cube(x): return x*x*x
cube_v2 = lambda x : x*x*x
print(cube(7)) # 343
print(cube_v2(7)) # 343
Functions within functions
def f1(name):
s = 'Hi ' + name
def f2():
print(s) # Hi abc
f2()
f1("abc")
Generators
Generator Functions
A generator function is defined like a normal function, but whenever it needs to generate a value, it does so with the yield keyword rather than return. If the body of a def contains yield, the function automatically becomes a generator function.
def simpleGeneratorFun():
yield 1
yield 2
yield 3
for value in simpleGeneratorFun():
print(value)
Generator Object
Generator functions return a generator object. Generator objects are used either by calling the next method on the generator object or using the generator object in a “for in” loop.
def fib(limit):
a, b = 0, 1 # Initialize first two Fibonacci Numbers
while a < limit:
yield a # One by one yield next Fibonacci Number
a, b = b, a + b
x = fib(5) # Create a generator object
''' either use next() '''
print(x.next()) # In Python 3, __next__() # 0
print(x.next()) # 1
print(x.next()) # 1
print(x.next()) # 2
print(x.next()) # 3
''' or iterable '''
for i in x:
print(i) # 0 1 1 2 3
Lambda
Anonymous function means that a function is without a name. def keyword
is used to define the normal functions and the lambda keyword is used to
create anonymous functions.
- can have any number of arguments but only one expression, which is evaluated and returned.
- One is free to use lambda functions wherever function objects are required.
- are syntactically restricted to a single expression.
- lambda returns a function object
''' syntax '''
lambda arguments : expression
''' example '''
x ="Hi there!"
(lambda x : print(x))(x)
''' lambda vs normal function '''
# normal function
def cube(y):
return y*y*y;
print(cube(5))
# lambda function
g = lambda x: x*x*x
print(g(7))
Local and Global Variables
local variables are accessible only inside
the function in which it was initialized whereas the
global variables are accessible throughout
the program and inside every function. to access
global variables inside a function we need to
re-declare them using the keyword global
def f():
global s # accesses the global variable 's'
s += ' there'
print(s) # Hi there
s = "Hello"
print(s) # Hello
# Global Scope
s = "Python is great!"
f()
print(s) # Hello
First Class Function
First class objects in a language are handled
uniformly throughout. They may be stored in data structures, passed as
arguments, or used in control structures. A programming language is said
to support first-class functions if it treats
functions as first-class objects.
- A function is an instance of the Object type.
- You can store the function in a variable.
- You can pass the function as a parameter to another function.
- You can return the function from a function.
- You can store them in data structures such as hash tables, lists, …
''' functions are objects '''
def shout(text):
return text.upper()
print (shout('Hello')) # HELLO
yell = shout
print (yell('Hello')) # HELLO
''' passing functions as argument '''
def shout(text):
return text.upper()
def whisper(text):
return text.lower()
def greet(func):
greeting = func("Hi there.")
print (greeting)
greet(shout) # HI THERE.
greet(whisper) # hi there.
''' function returning another function '''
def create_adder(x):
def adder(y):
return x+y
return adder
add_15 = create_adder(15)
print (add_15(10)) # 25
Closures
A Closure is a function object that remembers values in enclosing scopes
even if they are not present in memory.
Why and when to use Closures?
- It is a record that stores a function together with an environment: a mapping associating each free variable of the function (variables that are used locally but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.
- A closure—unlike a plain function—allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.
def outerFunction(text):
text = text
def innerFunction():
print(text)
return innerFunction
if __name__ == '__main__':
myFunction = outerFunction('Hey!')
myFunction() # Hey!
# The function innerFunction has its scope only inside the outerFunction. But with the use of closures, we can easily extend its scope to invoke a function outside its scope.
Why and when to use Closures?
- As closures are used as callback functions, they provide some sort of data hiding. This helps us to reduce the use of global variables.
- When we have few functions in our code, closures prove to be an efficient way. But if we need to have many functions, then go for class (OOP).
Decorators
Decorators allow us to wrap another function
in order to extend the behaviour of the wrapped function, without
permanently modifying it.
Introduction
def hello_decorator(func):
def inner1():
print("Hello, this is before function execution")
func()
print("This is after function execution")
return inner1
''' either use @hello_decorator '''
if __name__ == '__main__':
@hello_decorator
def function_to_be_used(): # defining a function, to be called inside wrapper
print("This is inside the function !!")
function_to_be_used()
''' or without using @hello_decorator '''
if __name__ == '__main__':
def function_to_be_used(): # defining a function, to be called inside wrapper
print("This is inside the function !!")
f = hello_decorator(function_to_be_used) # passing 'function_to_be_used' inside the decorator to control its behaviour
f()
Chained decorators
@decor2
@decor1
def function_to_be_used():
pass
# first function_to_be_used() is passed to decor1, then decor1(function_to_be_used()) is passed to decor2
# final result: decor2(decor1(function_to_be_used))
Decorators with parameters
def decorator(*args, **kwargs):
pass
@decorator(name = "abc")
def function_to_be_used():
pass
Memoization using decorators
Memoization is a technique of recording the intermediate results so that it can be used to avoid repeated calculations and speed up the programs. It can be used to optimize the programs that use recursion.
memory = {}
def memoize_factorial(f):
def inner(num):
if num not in memory:
memory[num] = f(num)
print('result saved in memory')
else:
print('returning result from saved memory')
return memory[num]
return inner
@memoize_factorial
def facto(num):
if num == 1:
return 1
else:
return num * facto(num-1)
print(facto(5))
print(facto(5)) # directly coming from saved memory
- In python, everything is passed by reference, except immutable objects which are passed by value.