map()
functionfilter()
functionreduce()
functionsorted()
functionzip()
functionThe syntax of defining a lambda function is:lambda [arg1 [,arg2,.....argn]]:expression
def square1(num):
result = num**2
return result
rv = square1(5)
rv
25
Let us try to shrink the above function
def square2(num):
return num**2
rv = square2(5)
rv
25
Although not a good programming style, however, we can write this in a single line
def square3(num): return num**2
rv = square3(5)
rv
25
This is the form a function that a lambda expression intends to replicate. A lambda expression can then be written as:
lambda num: num**2
<function __main__.<lambda>(num)>
Note how we get a function back. We can assign this function to a label:
square4 = lambda num: num**2
print(square4)
print(type(square4))
<function <lambda> at 0x7f8f138bda60> <class 'function'>
rv = square4(5)
rv
25
def mysum2(a, b):
return a+b
rv = mysum2(5, 7)
rv
mysum2 = lambda a, b: a + b
rv = mysum2(5.3, 7)
rv
12.3
def mysum3(a, b, c):
return a+b+c
rv = mysum3(5, 7, 9)
rv
mysum3 = lambda a, b, c: a + b + c
rv = mysum3(5.5, 6.3, 2.7) # return type corresponds to the expression
rv
14.5
even = lambda x: x % 2 == 0
rv = even(3) # return type corresponds to the expression
rv
False
func = lambda s: s[0]
rv = func("Hello")
rv
'H'
func = lambda s: s[::-1]
rv = func("Hello")
rv
'olleH'
func = lambda arg: len(arg)
list1 = ['Learning', 'is', 'fun', 'with', 'arif']
rv = func(list1)
rv
5
The real usage of Python Lambda functions is actually passing them as arguments to other functions like
map()
,filter()
andreduce()
Three regular function definitions and their calling convention
# Some basic functions that receives two arguments and return their sum, diff, mul
def myadd(a, b):
return a + b
def mysub(a, b):
return a - b
def mymul(a, b):
return a * b
# Calling above functions
rv1 = myadd(8,2)
rv2 = mysub(8, 2)
rv3 = mymul(8, 2)
rv1, rv2, rv3
(10, 6, 16)
We can write above three functions as lambda functions
myadd = lambda a, b: a+b
mysub = lambda a, b: a-b
mymul = lambda a, b: a*b
Now, let us write a calculator function that other than receiving two arguments, also receives a function name which specifies the operation that needs to be performed on the arguments
def mycalc(op, a, b):
return op(a,b)
rv1 = mycalc(myadd, 8, 2)
rv2 = mycalc(mysub, 8, 2)
rv3 = mycalc(mymul, 8, 2)
rv1, rv2, rv3
(10, 6, 16)
A more elegant way of writing above code
rv1 = mycalc(lambda a, b: a + b, 8, 2)
rv2 = mycalc(lambda a, b: a - b, 8, 2)
rv3 = mycalc(lambda a, b: a - b, 8, 2)
rv1, rv2, rv3
(10, 6, 6)
map()
Function¶map(aFunction, *iterables)
function simply returns a map object after applying aFunction()
to all the elements of iterable(s)
. Later you can type cast the map object to appropriate data structurehelp(map)
Help on class map in module builtins: class map(object) | map(func, *iterables) --> map object | | Make an iterator that computes the function using arguments from | each of the iterables. Stops when the shortest iterable is exhausted. | | Methods defined here: | | __getattribute__(self, name, /) | Return getattr(self, name). | | __iter__(self, /) | Implement iter(self). | | __next__(self, /) | Implement next(self). | | __reduce__(...) | Return state information for pickling. | | ---------------------------------------------------------------------- | Static methods defined here: | | __new__(*args, **kwargs) from builtins.type | Create and return a new object. See help(type) for accurate signature.
Given a list of numbers, suppose we want to create a new list in which every element is the square every item of the given list
Option 1: We can do this using a simple loop
mylist = [5, 7, 2, 6, 9]
mylist_squared = [] # create an empty list
for a in mylist:
mylist_squared.append(a**2) # append new item at the end of the newly created list
print("Original list: ", mylist)
print("List with items squared: ", mylist_squared)
Original list: [5, 7, 2, 6, 9] List with items squared: [25, 49, 4, 36, 81]
Option 2: We can do this using map()
function and passing an appropriate regular function as its first argument
mylist = [5, 7, 2, 6, 9]
def sqr(x):
return x ** 2
map_object = map(sqr, mylist)
mylist_squared = list(map_object)
print("Original list: ", mylist)
print("List with items squared: ", mylist_squared)
Original list: [5, 7, 2, 6, 9] List with items squared: [25, 49, 4, 36, 81]
sqr(x)
, to the map
function, along with the list of items on which to apply that functionmap()
function calls sqr()
function on each list item and collects all the return values into a map object, which is type casted to a listmap()
expects a function to be passed in, so this is where we can also use lambda functions as shown belowLet us use Lambda Function as key argument to map()
function to perform the above task
mylist = [5, 7, 2, 6, 9]
map_object = map(lambda x: x ** 2 , mylist)
mylist_squared = list(map_object)
print("Original list: ", mylist)
print("List with items squared: ", mylist_squared)
Original list: [5, 7, 2, 6, 9] List with items squared: [25, 49, 4, 36, 81]
Let us do this with List comprehension
mylist = [5, 7, 2, 6, 9]
newlist = [i**2 for i in mylist]
newlist
[25, 49, 4, 36, 81]
Given a list of numbers, suppose we want to create a new list in which every element is the remainder once the original list element is divided by 5
mylist = [74, 85, 14, 23, 56, 32, 45 ]
map_object = map(lambda num: num%5, mylist)
remainders = list(map_object)
print("Original list: ", mylist)
print("List of remainders: ", remainders)
Original list: [74, 85, 14, 23, 56, 32, 45] List of remainders: [4, 0, 4, 3, 1, 2, 0]
Suppose we want to add two lists
mylist1 = [4, 8, 3, 2]
mylist2 = [3, 1, 2, 6]
result = map(lambda a, b: a + b, mylist1, mylist2) #two arguments are passed to lambda func (one from each list)
result = list(result)
print("Sum of the two lists: ", result)
Sum of the two lists: [7, 9, 5, 8]
Given a list of strings, suppose we want to create another list that contains the length of each string in the list
list1 = ('Mujahid', 'Arif Butt', 'Kakamanna', 'Maaz')
result = map(lambda a: len(a), list1)
result = list(result)
print("Length of Strings in list1: ", result)
Length of Strings in list1: [7, 9, 9, 4]
filter()
Function¶filter(function or None, iterable)
filter()
function offers a convenient way to filter out all the elements of an iterable, for which the function returns true.function(item)
returns True.list()
functionA very basic usage of filter()
, that returns the True elements of a list
- Constants like
None
andFalse
- Numeric types having values: 0, 0.0, 0j
- Empty sequences and collections like
""
,()
,[]
,{}
,set()
, andrange(0)
mylist = [5, 0, -3, {}, False, 0.0, True, 9, 0j, (), None, 8]
result = filter(None, mylist)
print(result)
print(list(result))
<filter object at 0x7fd8c0644ee0> [5, -3, True, 9, 8]
Suppose we want to extract even numbers from a list
numbers = [1, 5, 4, 6, 8, 11, 3, 12]
result = filter(lambda x: x%2 == 0 , numbers)
result = list(result)
print("Even numbers in the list are: ", result)
Even numbers in the list are: [4, 6, 8, 12]
Let us do it using List Comprehension
numbers = [1, 5, 4, 6, 8, 11, 3, 12]
newlist = [i for i in numbers if i%2 == 0]
newlist
[4, 6, 8, 12]
Suppose we want to extract negative numbers from a list
numbers = [25, -3, -8, 17, 3, 8, -3, 6, -7, 0]
result = filter(lambda x: x<0, numbers)
result = list(result)
print("Negative numbers in the list are: ", result)
Negative numbers in the list are: [-3, -8, -3, -7]
Suppose we want to extract vowels from a list of alphabets
characters = ['i', 'z', 'b', 'a', 'd', 'f', 't', 'e','w', 'x']
vowels = ['a', 'e', 'i', 'o', 'u']
result = filter(lambda x: x in vowels, characters)
result = list(result)
print("Vowels in the list are: ", result)
Vowels in the list are: ['i', 'a', 'e']
reduce()
Function¶reduce()
works differently than map()
and filter()
. It does not return a new iterable based on the function and iterable we've passed. Instead, it returns a single value.reduce(func, sequence[, initial])
[ s1, s2, s3, ... , sn ]
, then calling reduce(func, seq)
works like this:func
argument to the first two items in the iterable and generate a partial result, i.e. func(s1,s2)
reduce()
works looks now like this: [ func(s1, s2), s3, ... , sn ]
func(func(s1, s2),s3)
[ function(function(s1, s2),s3), ... , sn ]
reduce()
initial
when provided is used as the 0th elementreduce()
, then you first have to import the function into your current scope using an import statement in one of the following ways:import functools
and then use fully-qualified names like functools.reduce().from functools import reduce
and then call reduce() directly.Suppose given a list of numbers and we want to get the accumulative sum of all the numbers in that list
Option 1: We can do this using a simple loop
numbers = [47, 11, 42, 13]
total = 0
for i in numbers:
total += i
total
113
Option 2: We can do this using reduce()
function and passing an appropriate regular function as its first argument. Remember this has to be a function that receives two arguments
#Example: Note the call to reduce(),
# applies myadd() to the items in the numbers list to compute their accumulative sum
from functools import reduce
def myadd(a,b):
return a+b
numbers = [47, 11, 42, 13]
rv = reduce(myadd, numbers)
rv
113
Option 3: Let us use Lambda Function as first argument to reduce()
function to perform the above task
# Example: Use lambda function for above task
from functools import reduce
numbers = [47, 11, 42, 13]
rv = reduce(lambda x,y: x+y, numbers)
rv
113
Let us now understand the initial argument to reduce()
function
reduce(func, sequence[, initial])
# Example: Use of initial argument of reduce() function
from functools import reduce
numbers = [47, 11, 42, 13]
rv = reduce(lambda x,y:x+y, numbers, 10)
rv
123
Multiplying all numeric values of a list with each other
from functools import reduce
numbers = [1, 2, 3, 4, 5]
rv = reduce(lambda x,y: x*y, numbers)
rv
120
Finding minimum or maximum number from a list of numbers
numbers = [-10, -20, -93, -4, -5]
rv1 = reduce(lambda a, b: a if a > b else b, numbers, 0)
rv1
0
from functools import reduce
numbers = [10, 20, 93, 4, 5]
rv1 = reduce(lambda a, b: a if a < b else b, numbers)
rv2 = reduce(lambda a, b: a if a > b else b, numbers)
rv1, rv2
(4, 93)
Checking if ALL values in an iterable are true
from functools import reduce
mylist = [0, 0, 1, 0, 0]
rv = reduce(lambda a, b: bool(a and b), mylist)
rv
False
Checking if ANY value in an iterable is true
from functools import reduce
mylist = [0, 0, 1, 0, 0]
rv = reduce(lambda a, b: bool(a | b), mylist)
rv
True
sorted()
Function¶sorted()
function in our previous session of Tuples.sorted()
function is an iterable.sorted(iterable, key=None, reverse=False)
We also have seen the use of key
argument, where we can pass a function that is applied once to each element of the iterable before sorting it.
Consider a tuple of strings ('abcz', 'xyza', 'bas', 'arif')
. If we pass this tuple to sorted()
function, it will sort the list alphabatically.
t1 = ('abcz', 'xyza', 'bas', 'arif')
rv = sorted(t1)
print("Sorted tuple: ", rv)
print("Original tuple remains as such: ", t1)
Sorted tuple: ['abcz', 'arif', 'bas', 'xyza'] Original tuple remains as such: ('abcz', 'xyza', 'bas', 'arif')
Suppose, we want to sort the above tuple by last character of strings within the tuple so that the output is like : ('xyza', 'arif', 'bas', 'abcz')
. We can do this by passing an appropriate regular function to key
argument of the sorted()
function
def last(s):
return s[-1]
t1 = ('abcz', 'xyza', 'bas', 'arif')
rv = sorted(t1, key=last)
print("Sorted tuple: ", rv)
print("Original tuple remains as such: ", t1)
Let us use Lambda Function as key
argument to sorted()
function to perform the above task
t1 = ('abcz', 'xyza', 'bas', 'arif')
rv = sorted(t1, key = lambda arg : arg[-1])
print("Sorted tuple: ", rv)
print("Original tuple remains as such: ", t1)
Sorted tuple: ['xyza', 'arif', 'bas', 'abcz'] Original tuple remains as such: ('abcz', 'xyza', 'bas', 'arif')
Suppose given a list in which each element is a two valued tuple [(4, 30), (6, 15), (1, 25), (9, 8)]
. If we call sorted()
function on this list it will sort it based on the first element of each tuple.
Simple sorting of above list using sorted()
function, will sort by first element
mylist = [(4, 30), (6, 15), (1, 25), (9, 8)]
mylist_sorted = sorted(mylist)
mylist_sorted
[(1, 25), (4, 30), (6, 15), (9, 8)]
But we want to sort the above list based on the second element of each tuple, so that the output is like: [(9, 8), (6, 15), (1, 25), (4, 30)]
. We can do this by passing an appropriate regular function to key
argument of the sorted()
function
mylist = [(4, 30), (6, 15), (1, 25), (9, 8)]
def func(item):
return item[1]
mylist_sorted = sorted(mylist, key = func)
mylist_sorted
[(9, 8), (6, 15), (1, 25), (4, 30)]
Let us use Lambda Function as key
argument to sorted()
function to perform the above task
mylist = [(4, 30), (6, 15), (1, 25), (9, 8)]
mylist_sorted = sorted(mylist, key = lambda element:element[1])
mylist_sorted
[(9, 8), (6, 15), (1, 25), (4, 30)]
zip()
Function¶zip(*iterables)
function returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables.x = [1,2,3]
y = [4,5,6]
zip_object = zip(x ,y)
mylist = list(zip_object)
mylist
[(1, 4), (2, 5), (3, 6)]
x = [1, 2, 3, 4]
y = [5, 6, 7, 8]
z = [9, 10, 11, 12]
zip_object = zip(x,y,z)
mylist = list(zip_object)
mylist
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
The iterator stops when the shortest input iterable is exhausted.
x = [1,2,3]
y = [4,5,6,7,8]
zip_object = zip(x,y)
mylist = list(zip_object)
mylist
[(1, 4), (2, 5), (3, 6)]
With a single iterable argument, it returns an iterator of 1-tuples.
x = [1, 2, 3, 4]
zip_object = zip(x)
mylist = list(zip_object)
mylist
[(1,), (2,), (3,), (4,)]
With no arguments, it returns an empty iterator.
zip_object = zip()
mylist = list(zip_object)
mylist
[]
Iterators:
for
loops, we discussed the concept of Iterator
object, which is used to iterate over iterable objects like lists and tuples.iter()
function, which is passed an iterable (list, tuple, ...) and it returns an iterator for that iterable object.next()
function, which is passed the iterator object, and each time it is called it returns the next item of that iterator object.Example: Iterator Objects
mylist = ['banana', 'mango', 'grapes']
a = iter(mylist)
a
<list_iterator at 0x7fd8c0845520>
print(next(a))
print(next(a))
print(next(a))
banana mango grapes
next(a)
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) /var/folders/1t/g3ylw8h50cjdqmk5d6jh1qmm0000gn/T/ipykernel_28226/1242322984.py in <module> ----> 1 next(a) StopIteration:
Generators:
return
statement, the generator function uses the yield
statementExample 1: Writing a Hello World Generator Function to understand the yield
keyword
def mygenerator():
print('First item')
yield 10
print('Second item')
yield 20
print('Last item')
yield 30
gen = mygenerator()
gen
<generator object mygenerator at 0x7fd8c08f7f20>
print(next(gen))
First item 10
print(next(gen))
Second item 20
print(next(gen))
Last item 30
Example 2: Writing a Generator Function that returns cubes of numbers
def gencubes(n):
for num in range(n):
yield num**3
for x in gencubes(5):
print(x)
0 1 8 27 64
The main advantage of using a generator function over the iterator is that elements are generated dynamically. Since the next item is generated only after the first is consumed, it is more memory efficient than the iterator.
Try answering the following questions to test your understanding of the topics covered in this notebook:
return
keyword in Python?return
statement be used inside an if
block or a for
loop?return
keyword be used outside a function?sys.argv[]
, and getopt.getopt()