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
Functions in Python are objects!

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(undecorated)

			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

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()
			
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")
		

Example with return value

The function to be decorated has to return value - but as it's executed inside wrapper, we need to explicitly return the value from wrapper. That is because, the wrapper will be our decorated function, after the decoration process.

			def log_decorator(f):

			  def wrapper(a,b):
				print("LOG: The function was executed!")
				return f(a,b)

			  return wrapper

			@log_decorator
			def sum(x,y):
			  return (x+y)


			print( sum(2,3) )
		

Readings

Improve Your Python: Decorators Explained by Jeff Knupp

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 function can contain only single expression.
Lambda functions are generally used with reduce( ), filter( ), and maps() - as discussed further, or as arguments to other functions - typical for Functional Programming paradigm.

Syntax

Lamda functions are defined by:

				lambda parameter_list: expression
			
which is equivalent to

				def some_func(parameter_list):
					return expression
			
Note, that in next example, the lambda function is assigned to a variable just to demonstrate the lambda syntax in simplest context. This is not Pythonic - lambdas are usually used as anonymous functions.

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

				maxnum = lambda x,y: x if x>y else y
				print( maxnum(30,40) )
			
Next example demonstrate a more Pythonic use of lambda function:

				calc = {
					'add': lambda x,y:x+y,
					'del': lambda x,y:y!=0 and x/y,
				}

				print( calc['add'](2,3) )
				print( calc['del'](2,3) )
			

The filter() Function

The filter() Function

./images/filter.png
filter() is a built-in function that allows to process an iterable and extract those items that satisfy a given condition

			filter(function, iterable)
		
function - a function definition.
iterable - a sequence (list, tupple, range or string) or container which supports iteration, or an iterator.
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) )
			

More examples

Example: filter empty strings

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

				print( list(not_empty_names) )
			
Example: Names, starting with 'A'

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

				print( list(filtered_names) )
			
Note that we can use list comprehenstions insead of filter

				names = ["Ivan", "", "Alex", "", "Maria", "Angel", ""]
				not_empty_names = [ name for name in names if name]

				print( list(not_empty_names) )
			

The map() Function

The map() Function

./images/map_function.png

Syntax

map() return an iterator that applies function to every item of iterable, yielding the results

				map(function, iterable, ...)
			
function - a function definition.
iterable - a sequence (list, tupple, range or string) or container which supports iteration, or an iterator. 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: map list of numbers to 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]
			

Examples

Example: Generate all uppercase cyrillic letters by their UTF codes

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

				#['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я']
			
Above example can be done with list comprehenstions, as well:

				letters = [chr(code) for code in range(1040, 1072)]
				print( list(letter

				#['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я']
			
Example: feed map() with multiple iterables. Note that this example can not be done easilly with list comprehensions

				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

./images/reduce.png
reduce() allows us to apply a function to an iterable and reduce it to a single cumulative value

				from functools import reduce

				reduce(function, iterable[, initializer])
			
In Python3, reduce() is not built-in, but we must import it from functools module!
function - a function definition.The first argument passed to function 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)
iterable - a sequence (list, tupple, range or string) or container which supports iteration, or an iterator (will be discussed in further topics).
If the optional initializer is present, it is placed before the items of the iterable in the calculation, and serves as a default when the iterable is empty
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5], 100) calculates (((((100+1)+2)+3)+4)+5)

Examples

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: sum the prices of all products

				products = [
					{'name':'apples', 'price': 2},
					{'name':'oranges', 'price': 5},
					{'name':'bananas', 'price': 3},
				]

				#note that here we must provide initializer
				total_price = reduce( lambda curr_price, product:curr_price+product['price'], products, 0)

				print(total_price)
			

Example: map - reduce

Often in practice filter, map and reduce are used in combination
Consider next example, which returns the sum of 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]
		

Exercises

Task1: get_names_with_posistive_balance

The Task

Create a list of all user names, which have positive balance

			users = [
				{'name':'Maria', 'balance': 2000},
				{'name':'Petar', 'balance': -189},
				{'name':'Ivan', 'balance': 3200},
				{'name':'Asen', 'balance': -2890},
			]

			# YOUR CODE here

			# EXPECTED OUTPUT:
			['Maria', 'Ivan']
		

Task2: map_filter_words_in_quotes

Next list of Douglas Adams quotes is given:

			quotes = [
				'Nothing travels faster than the speed of light, with the possible exception of bad news, which obeys its own special laws',
				'A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.'
			]
		
Write a program which will output in the console only the words that starts with letter 't' and are longer than 3 symbols

			TRAVELS
			THAN
			THAT
			TRYING
		

These slides are based on

customised version of

Hakimel's reveal.js

framework