0% found this document useful (0 votes)
19 views48 pages

Lambdas and Built in Functions

This document covers advanced Python function concepts including the use of *args and **kwargs for flexible function parameters, lambda functions for creating anonymous functions, and closures for maintaining state. It also discusses built-in functions like map, filter, and reduce, and emphasizes the importance of list comprehensions and various built-in functions for data manipulation. The document serves as a comprehensive guide for enhancing function capabilities in Python programming.

Uploaded by

Elşən Qurbanov
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views48 pages

Lambdas and Built in Functions

This document covers advanced Python function concepts including the use of *args and **kwargs for flexible function parameters, lambda functions for creating anonymous functions, and closures for maintaining state. It also discusses built-in functions like map, filter, and reduce, and emphasizes the importance of list comprehensions and various built-in functions for data manipulation. The document serves as a comprehensive guide for enhancing function capabilities in Python programming.

Uploaded by

Elşən Qurbanov
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

FUNCTIONS

PART II
OBJECTIVES
Use the * and ** operator as parameters to a function and
outside of a function
Leverage dictionary and tuple unpacking to create more
flexible functions
Understand what a lambda is and how they are used
Explain what closure is and how it works in Python
Use built in functions to sort, reverse and calculate
aggregate information
*args
A special operator we can pass to functions

Gathers remaining arguments as a tuple

This is just a parameter - you can call it


whatever you want!
Example

def sum_all_values(*args):
total = 0
for val in args:
total += val

return total

sum_all_values(1, 2, 3) # 6

sum_all_values(1, 2, 3, 4, 5) # 15
Another Example
def ensure_correct_info(*args):
if "Colt" in args and "Steele" in args:
return "Welcome back Colt!"

return "Not sure who you are..."

ensure_correct_info() # Not sure who you are...

ensure_correct_info(1, True, "Steele", "Colt")

The order does not matter!


**kwargs

A special operator we can pass to functions

Gathers remaining keyword arguments as a


dictionary

This is just a parameter - you can call it


whatever you want!
Example

def favorite_colors(**kwargs):
for key, value in kwargs.items():
print(f"{key}'s favorite color is {value}")

favorite_colors(rusty='green', colt='blue')

# rusty's favorite color is green


# colt's favorite color is blue
Another Example

def special_greeting(**kwargs):
if "Colt" in kwargs and kwargs["Colt"] == "special":
return "You get a special greeting Colt!"
elif "Colt" in kwargs:
return f"{kwargs["Colt"]} Colt!"

return "Not sure who this is..."

special_greeting(Colt='Hello') # Hello Colt!


special_greeting(Bob='hello') # Not sure who this is...
special_greeting(Colt='special') # You get a special greeting Colt!
Parameter Ordering
1. parameters
2. *args
3. default parameters
4. **kwargs
Combined Example
def display_info(a, b, *args, instructor="Colt", **kwargs):
return [a, b, args, instructor, kwargs]

display_info(1, 2, 3, last_name="Steele", job="Instructor")

[1, 2, (3,), 'Colt', {'job': 'Instructor', 'last_name': 'Steele'}]

What's going on with with that (3,) ?

When you have a tuple with one item - Python needs


to distinguish between parenthesis and a tuple!
Using * as an Argument:
Argument Unpacking
We can use * as an argument to a function to
"unpack" values

def sum_all_values(*args):
# there's a built in sum function - we'll see more later!
return sum(args)

sum_all_values([1, 2, 3, 4]) # nope...


sum_all_values((1, 2, 3, 4)) # this does not work either...

sum_all_values(*[1, 2, 3, 4]) # 10
sum_all_values(*(1, 2, 3, 4)) # 10
Using ** as an Argument:
Dictionary Unpacking
We can use ** as an argument to a function to "unpack"
dictionary values into keyword arguments

def display_names(first, second):


return f"{first} says hello to {second}"

names = {"first": "Colt", "second": "Rusty"}

display_names(names) # nope..

display_names(**names) "Colt says hello to Rusty"


Example with **
as an Argument
def display_names(first, second):
return f"{first} says hello to {second}"

names = {"first": "Colt", "second": "Rusty"}

display_names(names) # nope..

display_names(**names) "Colt says hello to Rusty"


YOUR
TURN
Lambdas
Normal functions have names...
def first_function():
return 'Hello!'

first_function() # 'Hello!'

first_function.__name__ # first_function'

But lambdas are anonymous functions!


first_lambda = lambda x: x + 5

first_lambda(10) # 15

first_lambda.__name__ # '<lambda>'
Lambda Syntax
lambda parameters : body of function
add_values = lambda x, y: x + y

multiply_values = lambda x, y: x + y

add_values(10, 20) # 30

multiply_values(10, 20) # 200


map
A standard function that accepts at least two arguments, a
function and an "iterable"
iterable - something that can be iterated over (lists, strings,
dictionaries, sets, tuples)

runs the lambda for each value in the iterable and returns a map
object which can be converted into another data structure
l = [1, 2, 3, 4]

doubles = list(map(lambda x: x * 2, l))

evens # [2, 4, 6, 8]
map in Action
l = [1,2,3,4]

doubles = list(map(lambda x: x*2, l))

evens # [2,4,6,8]

names = [
{'first':'Rusty', 'last': 'Steele'},
{'first':'Colt', 'last': 'Steele', },
{'first':'Blue', 'last': 'Steele', }
]

first_names = list(map(lambda x: x['first'], names))

first_names # ['Rusty', 'Colt', 'Blue']


filter
There is a lambda for each value in the iterable.
Returns filter object which can be converted into other iterables
The object contains only the values that return true to the lambda

l = [1,2,3,4]

evens = list(filter(lambda x: x % 2 == 0, l))

evens # [2,4]
Combining filter and map
Given this list of names:
names = ['Lassie', 'Colt', 'Rusty']

Return a new list with the string


"Your instructor is " + each value in the array,
but only if the value is less than 5 characters
list(map(lambda name: f"Your instructor is {name}",
filter(lambda value: len(value) < 5, names)))

# ['Your instructor is Colt']


What about
List Comprehension?
Given this list of names:
names = ['Lassie', 'Colt', 'Rusty']

Return a new list with the string:


"Your instructor is " + each value in the array,
but only if the value is less than 5 characters

[f"Your instructor is {name}" for name in names if len(name) < 5]


reduce
runs a function of two arguments cumulatively to the items of
iterable, from left to right, which reduces the iterable to a single value
from functools import reduce

l = [1,2,3,4]

product = reduce(lambda x, y: x * y, l)

l = [1,2,3,4]

total = reduce(lambda x, y: x + y, l, 10)

You will not be using reduce frequently so it's good to


know it exists, but you will not find yourself using it
since we have a better option in most cases
reduce or List Comprehension?
For almost all problems especially at this stage, use list
comprehension - you will see it far more in the wild

from functools import reduce

l = [1,2,3,4]

product = reduce(lambda x, y: x * y, l)
YOUR
TURN
Closures
Accessing variables defined in outer
functions after they have returned!

- private variables
- not using global variables
Example

Let's imagine we want a counter variable


and would like to keep track of it
"Public" Counter
count = 0

def counter():
global count
count += 1
return count

This works, but anyone can change count!


"Private" Counter

def counter():
count = 0
count += 1
return count

No one can change count directly, but it


keeps getting redefined!
Closures using nonlocal
def counter():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner

Here we're making a variable count inside the


counter function, which can only be accessed
by counter and inner.

Once we return inner, we can still remember


count through closure!
Closures using Objects
def counter():
counter.count = 0
def inner():
counter.count += 1
return counter.count
return inner

Here we're making a property on the counter function


which can only be accessed by counter and inner.

Once we return inner, we can still remember


the count property through closure!
Partial Application with Closures
def outer(a):
def inner(b):
return a+b
return inner

result = outer(10)

result(20) # 30

When you are just using (not modifying) a variable through


closure, you don't need to use nonlocal or objects!

You will see this pattern again when you learn


about decorators!
YOUR
TURN
Built-in
Functions
all
Return True if all elements of the iterable are
truthy (or if the iterable is empty)

all([0,1,2,3]) # False

all([char for char in 'eio' if char in 'aeiou'])

all([num for num in [4,2,10,6,8] if num % 2 == 0]) # True


any
Return True if any element of the iterable is
truthy. If the iterable is empty, return False.

any([0, 1, 2, 3]) # True

any([val for val in [1,2,3] if val > 2]) # True

any([val for val in [1,2,3] if val > 5]) # False


sorted

Returns a new sorted list from the items in iterable

# sorted (works on anything that is iterable)

more_numbers = [6,1,8,2]
sorted(more_numbers) # [1, 2, 6, 8]
print(more_numbers) # [6, 1, 8, 2]
reversed

Return a reverse iterator.

more_numbers = [6, 1, 8, 2]
reversed(more_numbers) # <list_reverseiterator at 0x1049f7da0>
print(list(reversed(more_numbers))) # [2, 8, 1, 6]

Use slices or .reverse!


YOUR
TURN
max
Return the largest item in an iterable or the
largest of two or more arguments.

# max (strings, dicts with same keys)

max([3,4,1,2]) # 4
max((1,2,3,4)) # 4
max('awesome') # 'w'
max({1:'a', 3:'c', 2:'b'}) # 3
min
Return the smallest item in an iterable or the
smallest of two or more arguments.

# min (strings, dicts with same keys)

min([3,4,1,2]) # 1
min((1,2,3,4)) # 1
min('awesome') # 'a'
min({1:'a', 3:'c', 2:'b'}) # 1
len
Return the length (the number of items) of an object. The
argument may be a sequence (such as a string, tuple, list, or
range) or a collection (such as a dictionary, set)

len('awesome') # 7
len((1,2,3,4)) # 4
len([1,2,3,4]) # 4
len(range(0,10) # 10

len({1,2,3,4}) # 4
len({'a':1, 'b':2, 'c':2} # 3
abs
Return the absolute value of a number. The argument may be
an integer or a floating point number.

abs(-5) # 5
abs(5) # 5
sum
Takes an iterable and an optional start.
Returns the sum of start and the items of an
iterable from left to right and returns the total.
start defaults to 0

sum([1,2,3,4]) # 10

sum([1,2,3,4], -10) # 0
round
Return number rounded to ndigits precision after the
decimal point. If ndigits is omitted or is None, it returns
the nearest integer to its input.

round(10.2) # 10
round(1.212121, 2) # 1.21
zip
Make an iterator that aggregates elements from each of the
iterables.
Returns an iterator of tuples, where the i-th tuple contains the i-
th element from each of the argument sequences or iterables.
The iterator stops when the shortest input iterable is exhausted.

first_zip = zip([1,2,3], [4,5,6])

list(first_zip) # [(1, 4), (2, 5), (3, 6)]

dict(first_zip) # {1: 4, 2: 5, 3: 6}
zip

five_by_two = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]

list(zip(*five_by_two))

[(0, 1, 2, 3, 4), (1, 2, 3, 4, 5)]

Very common when working with more


complex data structures!
YOUR
TURN
Recap
*args is useful for accepting a variable number of arguments
**kwargs is useful when accepting a variable number of
keyword arguments
you can use * to unpack argument values
you can use ** to unpack dictionary values
closures are very useful for private variables
lambdas are annonymous functions that are useful with map,
filter and reduce
map is useful for transforming lists into different lists of the
same size
filter is useful for transforming lists into lists of different sizes
Python has quite a few built in functions - make sure to spend
the time learning them!

You might also like