Functional programming in Python

First-class functions in Python

First-class functions in Python

Overview

Functions in Python are First-class function, i.e:
a function can be assigned to variable, or stored in other data structure (like list of functions)
a function can be passed as argument to another function
a function can be returned as value from another function

Example - a function can be assigned to variable


			def greet(name):
				print("Hello, {}".format(name))

			#a function can be assigned to variable:
			foo = greet
			greet("Maria")
			foo("Pesho")

			#OUTPUT
			# Hello, Maria
			# Hello, Pesho
		

Example - a function can be passed as argument to another function


			def greet(name):
				print("Hello, {}".format(name))

			# a function can be passed as argument to another function
			def wrapper(f, n):
				f(n)

			wrapper(greet, "Alex")

			#OUTPUT
			# Hello, Alex
		

Example - a function can be returned as value from another function


			#a function can be returned as value from another function
			def greet_wrapper(name):
				def wrapper():
					print("Hello, {}".format(name))

				return wrapper

			greet = greet_wrapper("Viktor")
			greet()
		

Decorators.

Decorators

Overview

A decorator is a software design pattern.
Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated
Simply said, decorators allows us to insert logic before and after a function is called.

How Decorator Pattern works in Python?

decorated = decorator(decorated)

			def stars_decorator(f):
				def wrapper():
					print("*" * 50)
					f()
					print("*" * 50)

				return wrapper

			def greet():
				print("Howdy World!")

			# let's decorate greet:
			greet = stars_decorator(greet)

			# and use it:
			greet()
		

the @ syntactic sugar - syntax

Python provides a simple syntax (@decorator_name) for calling higher-order functions.
A function decorator, with @ syntax, can be regarded as a "syntactic sugar" for a function that takes a function and returns a function

			#define the decorator:
			def decorator():
				pass

			# define a function to be decorated:
			@decorator
			def decorated():
				pass

			# just call the decorated function:
			decorated()
		

the @ syntactic sugar - example


			#define the decorator:
			def stars_decorator(f):
				def wrapper():
					print("*" * 50)
					f()
					print("*" * 50)

				return wrapper

			# let's decorate greet:
			@stars_decorator
			def greet():
				print("Howdy World!")

			# and use it:
			greet()
		

Decorator Pattern in Python - example with arguments

What about if we want the decorated function to take some arguments?


			def stars_decorator(f):
				def wrapper(n):
					print("*" * 50)
					f(n)
					print("*" * 50)

				return wrapper

			def greet(name):
				print("Howdy {}!".format(name))

			# let's decorate greet:
			greet = stars_decorator(greet)

			# and use it:
			greet("Pesho")
		

the @ syntactic sugar - example with arguments


			def stars_decorator(f):
				def wrapper(n):
					print("*" * 50)
					f(n)
					print("*" * 50)

				return wrapper

			# let's decorate greet:
			@stars_decorator
			def greet(name):
				print("Howdy {}!".format(name))


			# and use it:
			greet("Pesho")
		

Lambda expressions

Lambda expressions

Overview

Lambda expressions in Python are used to create short (one-line), anonymous functions.
Lambda expressions creates a function that does not use def and return.
The "body" of lambda can contain only single expression.
They are generally used with reduce( ), filter( ), and maps(), or as arguments to other functions.

Syntax

Lamda functions are defined by:


			lambda	parameter_list: expression
		

which is equivalent to


			def some_func(arguments):
				return expression
		

but without a name

simple example

Note, that in bellow examples, lambda functions are assigned to variables, just to demonstrate the lambda syntax in simplest context. This is not Pythonic - lambdas should be used as anonymous functions. And we will see real lambda usage in next slides.


			l = lambda x,y:x+y
			print( l(2,3) )

			maxnum = lambda x,y: x if x>y else y
			print( maxnum(30,40) )
		

The filter() Function

The filter() Function

Syntax


			filter(function, iterable)
		
function - a function definition. Note, that you must not call the function!
iterable - a sequence (list, tupple, range or string) or container which supports iteration, or an iterator (will be discussed in further topics)
Function should return a Boolean value. It is called for each item in the sequence and if it returns False, the item will be filtered

Example: List of even numbers - without lambda


			def is_even(x):
			  return True if x!=0 and x%2==0 else False

			even_numbers = filter(is_even, range(10))

			print( list(even_numbers) )

			# [2, 4, 6, 8]
		

Example: List of even numbers - with lambda


			even_numbers = filter(lambda x: x%2==0 if x!=0 else False, range(10))

			print( list(even_numbers) )
		

Example: filter empty strings - with lambda


			names = ["Ivan", "", "Alex", "", "Maria", "Angel", ""]
			not_empty_names = filter(lambda s: s, names)

			print( list(not_empty_names) )
		

Example: Names, starting with 'A' - with lambda


			names = ["Ivan", "Alex", "Maria", "Angel", ""]
			filtered_names = filter(lambda s: s and s[0]=="A", names)

			print( list(filtered_names) )
		

The map() Function

The map() Function

Syntax


			map(function, iterable, ...)
		
function - a function definition. Note, that you must not call the function!
iterable - a sequence (list, tupple, range or string) or container which supports iteration, or an iterator (will be discussed in further topics). Note, that you can pass multiple iterables!
map() applies the function to all the elements of the iterable. And returns a new iterable with the elements changed by the function.

Example: numbers => squared numbers

map numbers in a list to a list of squared numbers:


			numbers = [1,2,3,4,5]

			sqr_numbers = map(lambda x:x**2, numbers)

			print( list(sqr_numbers) )

			#[1, 4, 9, 16, 25]
		

Example: Generate all uppercase cyrillic letters by their UTF codes


			letters = map(chr, range(1040, 1072))
			print( list(letters) )

			#['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я']
		

Example: feed map() with multiple iterables

If we pass to map N iterables, then it will feed the function with N arguments, each from the respective iterable.

			l1 = [1,2,3]
			l2 = [1,2,3]

			l_sum = map(lambda a,b: a+b, l1, l2)
			print( list(l_sum) )

			# [2, 4, 6]
		

The reduce() Function

The reduce() Function

reduce() in Python2 and Python3

In Python3 you must import reduce from functools module!
from functools import reduce
In Python2, you can use reduce without importing anything.

Syntax


			reduce(function, iterable)
		
function - a function definition. Note, that you must not call the function!
iterable - a sequence (list, tupple, range or string) or container which supports iteration, or an iterator (will be discussed in further topics).
Applys function of two arguments cumulatively to the items of sequence, from left to right, so as to reduce the sequence to a single value.
The first argument is the accumulated value and the second argument is the update value from the sequence
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5).

Example: sum the numbers from 0 to 10, including


			from functools import reduce

			res = reduce(lambda a,b: a+b, range(11))

			print(res)

			#55
		

Example: map - reduce

Sum variable number of lists items positionally


			l1 = [1,2,3]
			l2 = [1,2,3]
			l3 = [1,2,3]
			l = map(lambda *t: reduce(lambda a,b: a+b, t) , l1, l2,l3)
			print( list(l) )

			# [3, 6, 9]
		

These slides are based on

customised version of

Hakimel's reveal.js

framework