About

Today I would like to share some straightforward example of these important built-in functions in Python:

They are some convenient function for us to deal with sequences, benefited by the world of functional programming, we can apply this functions into every single elements of a sequances, in order to reduce the time of explicit Loop. What's more, all this functions are pure function, both have return value, we can use this return value to repersent our expression.

So in layman term, why to use them is it cam much simplify our code, to execute those loop and iteration mission in a simple, elegance and efficient way.

map()

The map() function is going to return an iterator that applies function to every item of iterable, yielding the results. Such as list we can check whether an object have iterable attribute by using hasattr such as:

> >> a = [1, 2, 3, 4]
>>> hasattr(a, '__iter__')
>>> True
  • map() function simple syntex:map(func, iterable)* parameter: func is an function that map() pass to the every elements in the iterable object, the iterable is an object that has __iter__ attribute, so every elements can execute the func
  • return value: a map object

Sounds like complicated? let's see an example: Assume we have a list that contain 1 - 5 digits, we want to every number add 1, before map() function, most likely we will do this:

numbers = [1, 2, 3, 4, 5]
for i in range(0, len(numbers)):
    numbers[i] += 1
print(numbers)
[2, 3, 4, 5, 6]

Or, in another way:

numbers = [1, 2, 3, 4, 5]
# create empty list
result = []

for n in numbers:
    result.append(n+1)
print(result)
[2, 3, 4, 5, 6]

Obviously, no matter in which way, we all need to handle loop, so, we can try to use map() function:

def add_one(n):
    return n+1

numbers = [1, 2, 3, 4, 5]
result = map(add_one, numbers)
print(result)
print(type(result))
print(list(result))
<map object at 0x10a3c7150>
<class 'map'>
[2, 3, 4, 5, 6]

I believe you already notice the beauty of map(), we have achieved our purpose by not using loop, meanwhile, the code written in an elegent and simplicity way. So the map() function will return a map object if we planning to use this object in the future, this object type will help us to save the memory utilization, we use the getsizeof() function from sys to see the memory utilization of each object, map object and list

from sys import getsizeof
print(f'The size of map object in memory is {getsizeof(result)} bytes')
print(f'Convert it into list: {getsizeof(list(result))} bytes')
The size of map object in memory is 64 bytes
Convert it into list: 72 bytes

The requirement of object to passed in map() function is iterable so as long as the object has attribute of __iter__ it works, not only list, but also tuple, such as:

numbers = (1, 2, 3, 4, 5)
print(f"Is tuple numbers iterable? Answer: {hasattr(numbers, '__iter__')}")

result = map(add_one, numbers)
print(result)
print(type(result))
print(tuple(result))
Is tuple numbers iterable? Answer: True
<map object at 0x109bb9410>
<class 'map'>
(2, 3, 4, 5, 6)

Have you notice in order to achieved this, we need to create a function called add_one(n)? and it just simply return n+1, possible to reduce this? to make the code more simply and elegent? Yes, we can use lambda

numbers = (1, 2, 3, 4, 5)
result = map(lambda x: x + 1, numbers)
print(tuple(result))
(2, 3, 4, 5, 6)

Three lines of code, simple, elegent, Pythonic

Beside using defined function or lambda function to execute the iterable, we also can utilize Python bulit-in function, bulit-in type to execute the iterable, such this case:

words = ['Singapore', 'Guangzhou', 'Tokyo']

# convert every elements in the array into List
converted = list(map(list, words))
print(converted)
print(f"The type of converted: {type(converted)}")
print(f"The lenght of converted: {len(converted)}")
[['S', 'i', 'n', 'g', 'a', 'p', 'o', 'r', 'e'], ['G', 'u', 'a', 'n', 'g', 'z', 'h', 'o', 'u'], ['T', 'o', 'k', 'y', 'o']]
The type of converted: <class 'list'>
The lenght of converted: 3

words is a list that contain string type of elements, we can use map() and Python bulit-in list to convert every elements in words into List, but do take note, every elements must have __iter__ attribute, otherwise, it will raise TypeError, such as int type:

numbers = [3, '23', 42]
print(list(map(list, numbers)))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-58446c133a68> in <module>
      1 numbers = [3, '23', 42]
----> 2 print(list(map(list, numbers)))

TypeError: 'int' object is not iterable

We can see: TypeError, int object is not iterable, we can avoid it by this way:

numbers = [3, '23', 42]
print(list(map(float, numbers)))
[3.0, 23.0, 42.0]

filter()

filter() function is using a function to "filter" the sequence, the function is going to examinate every elements in the sequence is True or False

  • filter() syntex: filter(func, iterable)
  • Parameter: func test iterable sequances' elements is True or False, iterable is the iterable sequances that been filter
  • Return value: an iterable sequance that every elements is True to the filter function func

Layman term: filter() is to filter a set of data based on the given conditions

Example:

def func(variable):
    letters = ['a', 'e', 'i', 'o', 'u']
    if (variable.lower() in letters):
        return True
    else:
        return False

# given sequance
sequance = ['I', 'l', 'o', 'v', 'e', 'p', 'y', 't', 'h', 'o', 'n']

filtered = list(filter(func, sequance))
print(f"The vowel in the sequance is {filtered}")
The vowel in the sequance is ['I', 'o', 'e', 'o']

Above we create a method to pull the vowel from a given sequance, the given sequance is List, so it have '__iter__', and apply it to filter() to pull out the vowel.

Here we have another example:

def positive(num):
    if num > 0:
        return True
    else:
        return False

# odd or even number
def even_number(num):
    if num % 2 == 0:
        return True
    else:
        return False

numbers = [1, -3, 5, -20, 0, 9, 12]
positive_number = list(filter(positive, numbers))
even_number = list(filter(even_number, numbers))

print(f"The positive number is: {positive_number}.")
print(f"The even number is {even_number}.")
The positive number is: [1, 5, 9, 12].
The even number is [-20, 0, 12].

So, how to use filter() function is quite simple:

  1. define a method that can filter out True or False
  2. apply it to iterable object
  3. Integrate it into your bigger code block

Now let's see how to use lambda together:

numbers = [0, 1, 2, -3, 5, -8, 42]

# odd number
odd_number = filter(lambda x: x % 2, numbers)
print(f"The odd number is {list(odd_number)}.")

# even number
even_number = filter(lambda x: x % 2 == 0, numbers)
print(f"The even number is {list(even_number)}.")

# positive number
positive_number = filter(lambda x: x > 0, numbers)
print(f"The positive number is {list(positive_number)}.")
The odd number is [1, -3, 5].
The even number is [0, 2, -8, 42].
The positive number is [1, 2, 5, 42].

Always remember the Python philosophy: efficient, simple, elegent

Reduce()

reduce() is very useful built-in function, it can execuate iterable object's compuatation and return the result, it can rolling compute the continues values in an iterable sequance, such as cumulative product of integer list, or cumulative sum.

  • Syntex: reduce(func, iterable)
  • Parameter: func: a continues method to execuate on each element of the iterable object, last resut is the new parameter of next execuation.
  • Return value: the func return value

In Python 3, reduce() moved to functools module, so before we use it, we need to import it from functools

Example:

from functools import reduce

def do_sum(num1, num2):
    return num1 + num2

print(f"The sum of 1, 2, 3, 4 is: {reduce(do_sum, [1, 2, 3, 4])}.")
The sum of 1, 2, 3, 4 is: 10.
def multiply(num1, num2):
    return num1*num2

print(f"The cumulative product of 1, 2, 3, 4 is: {reduce(multiply, [1, 2, 3, 4])}.")
The cumulative product of 1, 2, 3, 4 is: 24.
numbers = [1, 2, 3, 4]

result_multiply = reduce(lambda x, y: x*y, numbers)
result_sum = reduce(lambda x, y: x+y, numbers)

print(f"The cumulative product of 1, 2, 3, 4 is: {result_multiply}")
print(f"The cumulative sum of 1, 2, 3, 4 is: {result_sum}.")
The cumulative product of 1, 2, 3, 4 is: 24
The cumulative sum of 1, 2, 3, 4 is: 10.

zip()

As it's name, zip() function is to put multiple iterable object together, and "packed" it as one single object, mapping with similar index.

  • Syntex: zip(*iterators)
  • Parameter: iterators is iterable object, such as List, String
  • Return value: Single iterator object, containing index value from the packed object.

Example:

keys = ['name', 'age']
values = ['Apple', '44']

apple_dict = dict(zip(keys, values))
print(apple_dict)
{'name': 'Apple', 'age': '44'}

zip() it also support multiple objects:

names = ['Apple', 'Google', 'Microsoft']
ages = ['44', '21', '44']
values = ['100', '80', '60']

mapped_values = list(zip(names, ages, values))
print(mapped_values)
[('Apple', '44', '100'), ('Google', '21', '80'), ('Microsoft', '44', '60')]

We can use the zip() function to easily packed the values have same index from 3 list

But how about unpack?

Simple, just similar to unpanc tuple, we add the * to the object that we want to unpack

names, ages, values = zip(*mapped_values)
print(f"The names is {names}")
print(f"The ages is {ages}")
print(f"The values is {values}")
The names is ('Apple', 'Google', 'Microsoft')
The ages is ('44', '21', '44')
The values is ('100', '80', '60')

lambda()

While normal functions are defined using the def keyword, in Python anonymous functions are defined using the lambda keyword. Hence, anonymous functions are also called lambda functions.

Lambda function can use any quantity of parameter, but only have one expression

  • Syntex:lambda argument: manipulate(argument)* Parameter: argument is the passing parameter, after the : is the manipulate

Example:

add_one = lambda x: x+1
add_sum = lambda x, y: x+y

print(add_one(2))
print(add_sum(5, 5))
3
10

Normally we will not use lambda function individually, we will use it along with other built-in function or def function, which we have already shows above, use it along with map(), filter(), reduce() and zip() function.

Let's see one more example of lambda interacting with dict

university = [{'name': 'NYU', 
               'city': 'New York'}, 
              {'name': 'NUS', 
               'city': "Singapore"}]

names = list(map(lambda x: x['name'], university))
print(names)
['NYU', 'NUS']

Above we interacting with dict with map() function, given the condition for the iterable list of dict that contain the name and the city of each University

We also can interacting dict with filter() function, through the return value True or False to judge the filtering condition.

university = [{'name': 'NYU', 
               'city': 'New York'}, 
              {'name': 'NUS', 
               'city': "Singapore"}]

names = list(filter(lambda x: x['name'] == 'NUS', university))
print(names)
[{'name': 'NUS', 'city': 'Singapore'}]

Through the above example, you have seen the actual application scenario of lambda, but here I want to share my views with you: I think that the disadvantages of lambda are slightly more than the advantages, and you should avoid overusing lambda.

First of all, this is just my personal opinion. I hope everyone understands why I said this. First, let's compare the lambda method with the conventional def. I find that the main differences between lambda and def are as follows:

  • Passing the parameter immediately(no need to define variable).
  • one line of code, very simple (but, it doesn't mean is efficient).
  • Automatically return, no need return keyword.
  • lambda function DO NOT have a function name

You can see the advantages. I mainly want to talk about its disadvantages. First of all, starting from real needs, we don’t need lambda most of the time, because we can always find better alternatives. Now let ’s take a look In the example of lambda + reduce(), the result we achieved with lambada is as follows:

from functools import reduce          
numbers = [1,2,3,4]
result_multiply = reduce((lambda x, y: x * y), numbers)
result_add = reduce((lambda x,y: x+y), numbers)

Above example, the lambda didn't achieved the simple and efficient purpose, as we have the bulit-in sum and mul method, even it can work with NumPy also

from functools import reduce
import operator
import numpy as np

numbers = [1, 2, 3, 4]
result_sum = sum(numbers)
result_multiply = reduce(operator.mul, numbers)

print(f"The sum is {result_sum}")
print(f"The cumulative product is: {result_multiply}")

matrixA = np.random.randn(3, 3)
matrixB = np.random.randn(3, 3)
matrixList = [matrixA, matrixB]

mulmat = reduce(operator.mul, matrixList)
print(f"The Matrix Multipication is \n {mulmat}")
The sum is 10
The cumulative product is: 24
The Matrix Multipication is 
 [[-2.09128347 -0.96279072 -1.81723096]
 [-1.26743106  0.45465535  0.47941216]
 [-0.36291425 -0.02198572 -0.52437492]]

The result is the same, but obviously the solution using sum and mul is more efficient. Another common example shows that if we have a list that stores various colors, we now need to capitalize the first letter of each color. If we write it in lambda:

colors = ['red','purple','green','blue']
result = map(lambda c:c.capitalize(),colors)
print(list(result))
['Red', 'Purple', 'Green', 'Blue']

Seems ok, but, did we forgot the power of List ?

colors = ['red','purple','green','blue']
result = [c.capitalize() for c in colors]
print(result)
['Red', 'Purple', 'Green', 'Blue']

Sorted can also handle the case of irregular initials, saving even more time:

colors = ['Red','purple','Green','blue']
print(sorted(colors,key=str.capitalize))
['blue', 'Green', 'purple', 'Red']

There is another reason: lambda functions do not have function names. So in the case of code transfer and project migration, it will bring a lot of difficulties to the team. It is not bad to write a function add_one(), because everyone is easy to understand and know that it is a function of performing +1, but if you are in the team, use a lots of lambda will make peoples confused.

Scenario that fit lambda

  1. Your method is too simple to have a name, such as adding 1, or just stiching the strings
  2. Using lambda is much easier for people to understand
  3. There's no any Python built-in function except lambda
  4. Team members are all well understand lambda, most importantly, no one complain you.