Intermediate- Python Developer Interview - part 3

Intermediate- Python Developer Interview - part 3

Lambda functions, Map, Filter, Sorted, Reduce, Collections

·

9 min read

Photo by Amélie Mourichon on Unsplash

Lambda Functions

Lambda functions are also called as anonymous function. So why these functions are called as anonymous🤔

Here is the reason:

They are actually defined without any name. Below is the syntax for defining lambda functions:

lambda arguments: expression

Example of lambda function:

multiply = lambda x,n: x * n

print(multiply (5,2))

So in above function you can see we are giving 5 and 2 as arguments to the multiplication expression. Lambda functions can be used wherever function objects are required. They are syntactically restricted to a single expression.

Commonly, lambda functions are used in combination with filter, map, sort, reduce functions. The details of these are as follows.

Filter

The name itself suggests that it is filtering the values. As per official documentation

Filter: Construct an iterator from those elements of iterable for which function returns true. iterable may be either a sequence, a container which supports iteration, or an iterator. If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.

Syntax:

filter(function, iterable)

Using Lambda function with filter:

# Program to filter out items divisible by 10
numbers = [65, 78, 100, 54, 40]

new = list(filter(lambda x: (x%10 == 0) , numbers))

print(new)

In above example we can see that lambda function is checking if number is divisible by 10 or not and then filter is helping to filter out those particular numbers which are divisible by 10 from the given list.

Map

As per official documentation ,

Map : Return an iterator that applies function to every item of iterable, yielding the results. If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel.

Syntax:

map(function, iterable, ...)

Using lambda function with map:

my_list = [1, 6, 9, 3, 12]

new_list = list(map(lambda x: x * x , my_list))

print(new_list)

Sorted

sorted() built-in function in Python gives a new sorted list from an iterable. Has two optional arguments which must be specified as keyword arguments.

key specifies a function of one argument that is used to extract a comparison key from each element in iterable (for example, key=str.lower). The default value is None (compare the elements directly).

reverse is a boolean value. If set to True, then the list elements are sorted as if each comparison were reversed.

Syntax:

sorted(iterable, *, key=None, reverse=False)

Using lambda function with Sorted:

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]
print(sorted(student_tuples, key=lambda student: student[2]) )

Output:

[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

In above example we are sorting the iterable with respect to tuple's 3rd element.

Reduce

reduce() works by applying a two-argument function to the items of iterable in a loop from left to right, ultimately reducing iterable to a single cumulative value.

According to the official documentation

Syntax:

functools.reduce(function, iterable[, initializer])

Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable to a single value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5). The left argument, x, is the accumulated value and the right argument, y, is the update value from the iterable. 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. If initializer is not given and iterable contains only one item, the first item is returned.

Using lambda function with reduce:

from functools import reduce

numbers = [1, 2, 3, 4]

print(reduce(lambda a, b: a + b, numbers))

Output:

10

Here if you see elements from numbers list are added as per definition in lambda function. The result is similar to the builtin function sum() in Python. Using sum() is more pythonic way of adding all the elements in the list.

Now let's see what are collections in python and how we can use them.

Collections:

This module implements specialized container datatypes providing alternatives to Python’s general purpose built-in containers, dict, list, set, and tuple.

  • namedtuple() : factory function for creating tuple subclasses with named fields

Returns a new tuple subclass named typename. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable. Instances of the subclass also have a helpful docstring (with typename and field_names) and a helpful repr() method which lists the tuple contents in a name=value format. Syntax:

collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)

Use of namedtuple as per official documentation:

>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)
  • deque : list-like container with fast appends and pops on either end

Returns a new deque object initialized left-to-right (using append()) with data from iterable. If iterable is not specified, the new deque is empty.

Syntax:

class collections.deque([iterable[, maxlen]])

Important points:

  • Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.
  • Though list objects support similar operations, they are optimized for fast fixed-length operations and incur O(n) memory movement costs for pop(0) and insert(0, v) operations which change both the size and position of the underlying data representation.

  • If maxlen is not specified or is None, deques may grow to an arbitrary length. Otherwise, the deque is bounded to the specified maximum length. Once a bounded length deque is full, when new items are added, a corresponding number of items are discarded from the opposite end.

Use of deque as per official documentation:

>>> from collections import deque
>>> d = deque('ghi')                 # make a new deque with three items
>>> for elem in d:                   # iterate over the deque's elements
...     print(elem.upper())
G
H
I

>>> d.append('j')                    # add a new entry to the right side
>>> d.appendleft('f')                # add a new entry to the left side
>>> d                                # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])

>>> d.pop()                          # return and remove the rightmost item
'j'
>>> d.popleft()                      # return and remove the leftmost item
'f'
>>> list(d)                          # list the contents of the deque
['g', 'h', 'i']
>>> d[0]                             # peek at leftmost item
'g'
>>> d[-1]                            # peek at rightmost item
'i'

>>> list(reversed(d))                # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d                         # search the deque
True
>>> d.extend('jkl')                  # add multiple elements at once
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1)                      # right rotation
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1)                     # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])

>>> deque(reversed(d))               # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear()                        # empty the deque
>>> d.pop()                          # cannot pop from an empty deque
Traceback (most recent call last):
    File "<pyshell#6>", line 1, in -toplevel-
        d.pop()
IndexError: pop from an empty deque

>>> d.extendleft('abc')              # extendleft() reverses the input order
>>> d
deque(['c', 'b', 'a'])
  • ChainMap : dict-like class for creating a single view of multiple mappings

A ChainMap groups multiple dicts or other mappings together to create a single, updateable view. If no maps are specified, a single empty dictionary is provided so that a new chain always has at least one mapping.

The underlying mappings are stored in a list. That list is public and can be accessed or updated using the maps attribute.

Lookups search the underlying mappings successively until a key is found. In contrast, writes, updates, and deletions only operate on the first mapping.

Syntax:

class collections.ChainMap(*maps)

Use of ChainMap:

from collections import ChainMap


d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}


# Defining the chainmap
combo = ChainMap(d1, d2)

print(combo)
  • Counter : dict subclass for counting hashable objects

A Counter is a dict subclass for counting hashable objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts. The Counter class is similar to bags or multisets in other languages.

Syntax:

class collections.Counter([iterable-or-mapping])

Important points

  • The Counter class itself is a dictionary subclass with no restrictions on its keys and values. The values are intended to be numbers representing counts, but you could store anything in the value field.
  • Counter objects support three methods beyond those available for all dictionaries:

elements()

Return an iterator over elements repeating each as many times as its count. Elements are returned in the order first encountered. If an element’s count is less than one, elements() will ignore it.

c = Counter(a=4, b=2, c=0, d=-2)
sorted(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']

most_common([n])

Return a list of the n most common elements and their counts from the most common to the least. If n is omitted or None, most_common() returns all elements in the counter. Elements with equal counts are ordered in the order first encountered:


Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]

subtract([iterable-or-mapping])

Elements are subtracted from an iterable or from another mapping (or counter). Like dict.update() but subtracts counts instead of replacing them. Both inputs and outputs may be zero or negative.


c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

Use of Counter:

#Elements are counted from an iterable or initialized from another mapping (or counter):

c = Counter()                           # a new, empty counter
c = Counter('gallahad')                 # a new counter from an iterable
c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
c = Counter(cats=4, dogs=8)             # a new counter from keyword args
  • OrderedDict : dict subclass that remembers the order entries were added

Return an instance of a dict subclass that has methods specialized for rearranging dictionary order.

popitem(last=True)

The popitem() method for ordered dictionaries returns and removes a (key, value) pair. The pairs are returned in LIFO order if last is true or FIFO order if false.

move_to_end(key, last=True)

Move an existing key to either end of an ordered dictionary. The item is moved to the right end if last is true (the default) or to the beginning if last is false. Raises KeyError if the key does not exist

For more details on this refer the official documentation

  • defaultdict : dict subclass that calls a factory function to supply missing values

  • UserDict : wrapper around dictionary objects for easier dict subclassing

  • UserList : wrapper around list objects for easier list subclassing
  • UserString : wrapper around string objects for easier string subclassing

Please refer the respective links for detailed information of each data type mentioned above.

What we learned in this article:

  • What are lambda functions

  • How to use lambda functions in combination with map, filter, Sorted, Reduce

  • What are collections

References

docs.python.org/3/library