Some examples on functions in Python

In the previous class, we have already discussed functions, arguments, default values and kwargs. Here are some more examples.


In [3]:
def swap(a, b):
    a, b = b, a

x, y = 1, 2
print("Before swap, x = %d and y = %d." % (x, y))
swap(x, y)
print("After swap, x = %d and y = %d." % (x, y))


Before swap, x = 1 and y = 2.
After swap, x = 1 and y = 2.

Moral: functions are pass-by-value.

Functions are objects!

  • Functions are first class objects
  • You can pass them around as arguments to other functions or assign them to variables

In [9]:
def add_function_of_integers(func, upto):
    total = 0
    for n in range(upto + 1):
        total = total + func(n)
    return total

def identity(n):
    return n

N = 10

print("Sum of integers up to %d is %d." %
      (N, add_function_of_integers(identity, N)))

def square(n):
    return n * n

print("Sum of the squares of integers up to %d is %d." %
      (N, add_function_of_integers(square, N)))


Sum of integers up to 10 is 55.
Sum of the squares of integers up to 10 is 385.

Anonymous functions

  • Can create these on the fly
  • Also called lambda functions

In [10]:
N = 3
print("Sum of cubes of integers up to %d is %d." %
      (N, add_function_of_integers(lambda x : x**3, N)))


Sum of cubes of integers up to 3 is 36.

In [16]:
#def cube(x):
#    return x**3

cube = lambda x : x**3
cube(10)

add_two_numbers = lambda x, y : x + y
add_two_numbers(10, 20)

# To find the sum 1 + 1 + 1 ... + 1
print("Sum of the %d ones is %d." %
      (N + 1, add_function_of_integers(lambda x : 1, N)))


Sum of the 4 ones is 4.

List comprehensions

  • Allow you to create lists using a set builder like notation
  • Find and store the squares of the first N whole numbers

In [20]:
n_squares = []
for i in range(N + 1):
    n_squares.append(square(i))
print(n_squares)


[0, 1, 4, 9]

In [26]:
whole_numbers = range(N + 1)
n_cubes = [i * i * i for i in whole_numbers]
n_cubes


Out[26]:
[0, 1, 8, 27, 64, 125, 216]

In [35]:
N = 6
# Express dice rolls as (first die face, second die face)
cartesian_product = [(a, b)
                     for a in range(1, 7)
                     for b in range(1, 7)]
print(cartesian_product)
odd_sum_cases = [(a, b)
                     for a in range(1, 7)
                     for b in range(1, 7)
                if (a + b) % 2 == 1]
print(odd_sum_cases)


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

In [33]:
list_of_numbers = range(11)
odd_squares = [i * i
               for i in list_of_numbers
               if i % 2 == 1]
print(odd_squares)


[1, 9, 25, 49, 81]

In [40]:
# Using map
list_of_squares = list(map(lambda x : x * x,
                     list_of_numbers))

print(list_of_squares)


[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

In [49]:
# We will use filter on the dice pairs to print
# only those that sum to an even number
even_sum = filter(lambda x :
                       (x[0] + x[1]) % 2 == 0,
                  cartesian_product)

for die_rolls in even_sum:
    print("%d and %d sum to even!" %
          (die_rolls[0], die_rolls[1]))

print("Let's try printing it again!")
for die_rolls in even_sum:
    print("%d and %d sum to even!" %
          (die_rolls[0], die_rolls[1]))


1 and 1 sum to even!
1 and 3 sum to even!
1 and 5 sum to even!
2 and 2 sum to even!
2 and 4 sum to even!
2 and 6 sum to even!
3 and 1 sum to even!
3 and 3 sum to even!
3 and 5 sum to even!
4 and 2 sum to even!
4 and 4 sum to even!
4 and 6 sum to even!
5 and 1 sum to even!
5 and 3 sum to even!
5 and 5 sum to even!
6 and 2 sum to even!
6 and 4 sum to even!
6 and 6 sum to even!
Let's try printing it again!

Example: Ramanujan number

  • Find the smallest number that can be expressed as the cube of two natural numbers in two different ways

In [52]:
pairs_of_numbers = [(a, b) for a in range(1, 21)
                   for b in range(1, 21)]
pairs_of_numbers = list(filter(lambda x: x[0] <= x[1],
                              pairs_of_numbers))
print(pairs_of_numbers)


[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12), (1, 13), (1, 14), (1, 15), (1, 16), (1, 17), (1, 18), (1, 19), (1, 20), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (2, 15), (2, 16), (2, 17), (2, 18), (2, 19), (2, 20), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (3, 10), (3, 11), (3, 12), (3, 13), (3, 14), (3, 15), (3, 16), (3, 17), (3, 18), (3, 19), (3, 20), (4, 4), (4, 5), (4, 6), (4, 7), (4, 8), (4, 9), (4, 10), (4, 11), (4, 12), (4, 13), (4, 14), (4, 15), (4, 16), (4, 17), (4, 18), (4, 19), (4, 20), (5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11), (5, 12), (5, 13), (5, 14), (5, 15), (5, 16), (5, 17), (5, 18), (5, 19), (5, 20), (6, 6), (6, 7), (6, 8), (6, 9), (6, 10), (6, 11), (6, 12), (6, 13), (6, 14), (6, 15), (6, 16), (6, 17), (6, 18), (6, 19), (6, 20), (7, 7), (7, 8), (7, 9), (7, 10), (7, 11), (7, 12), (7, 13), (7, 14), (7, 15), (7, 16), (7, 17), (7, 18), (7, 19), (7, 20), (8, 8), (8, 9), (8, 10), (8, 11), (8, 12), (8, 13), (8, 14), (8, 15), (8, 16), (8, 17), (8, 18), (8, 19), (8, 20), (9, 9), (9, 10), (9, 11), (9, 12), (9, 13), (9, 14), (9, 15), (9, 16), (9, 17), (9, 18), (9, 19), (9, 20), (10, 10), (10, 11), (10, 12), (10, 13), (10, 14), (10, 15), (10, 16), (10, 17), (10, 18), (10, 19), (10, 20), (11, 11), (11, 12), (11, 13), (11, 14), (11, 15), (11, 16), (11, 17), (11, 18), (11, 19), (11, 20), (12, 12), (12, 13), (12, 14), (12, 15), (12, 16), (12, 17), (12, 18), (12, 19), (12, 20), (13, 13), (13, 14), (13, 15), (13, 16), (13, 17), (13, 18), (13, 19), (13, 20), (14, 14), (14, 15), (14, 16), (14, 17), (14, 18), (14, 19), (14, 20), (15, 15), (15, 16), (15, 17), (15, 18), (15, 19), (15, 20), (16, 16), (16, 17), (16, 18), (16, 19), (16, 20), (17, 17), (17, 18), (17, 19), (17, 20), (18, 18), (18, 19), (18, 20), (19, 19), (19, 20), (20, 20)]
  • Now we form a new list with the sum and the actual tuples linked together in a tuple

In [53]:
numbers_with_cube_sums = [(a[0]**3 + a[1]**3,a)
                         for a in pairs_of_numbers]
print(numbers_with_cube_sums)


[(2, (1, 1)), (9, (1, 2)), (28, (1, 3)), (65, (1, 4)), (126, (1, 5)), (217, (1, 6)), (344, (1, 7)), (513, (1, 8)), (730, (1, 9)), (1001, (1, 10)), (1332, (1, 11)), (1729, (1, 12)), (2198, (1, 13)), (2745, (1, 14)), (3376, (1, 15)), (4097, (1, 16)), (4914, (1, 17)), (5833, (1, 18)), (6860, (1, 19)), (8001, (1, 20)), (16, (2, 2)), (35, (2, 3)), (72, (2, 4)), (133, (2, 5)), (224, (2, 6)), (351, (2, 7)), (520, (2, 8)), (737, (2, 9)), (1008, (2, 10)), (1339, (2, 11)), (1736, (2, 12)), (2205, (2, 13)), (2752, (2, 14)), (3383, (2, 15)), (4104, (2, 16)), (4921, (2, 17)), (5840, (2, 18)), (6867, (2, 19)), (8008, (2, 20)), (54, (3, 3)), (91, (3, 4)), (152, (3, 5)), (243, (3, 6)), (370, (3, 7)), (539, (3, 8)), (756, (3, 9)), (1027, (3, 10)), (1358, (3, 11)), (1755, (3, 12)), (2224, (3, 13)), (2771, (3, 14)), (3402, (3, 15)), (4123, (3, 16)), (4940, (3, 17)), (5859, (3, 18)), (6886, (3, 19)), (8027, (3, 20)), (128, (4, 4)), (189, (4, 5)), (280, (4, 6)), (407, (4, 7)), (576, (4, 8)), (793, (4, 9)), (1064, (4, 10)), (1395, (4, 11)), (1792, (4, 12)), (2261, (4, 13)), (2808, (4, 14)), (3439, (4, 15)), (4160, (4, 16)), (4977, (4, 17)), (5896, (4, 18)), (6923, (4, 19)), (8064, (4, 20)), (250, (5, 5)), (341, (5, 6)), (468, (5, 7)), (637, (5, 8)), (854, (5, 9)), (1125, (5, 10)), (1456, (5, 11)), (1853, (5, 12)), (2322, (5, 13)), (2869, (5, 14)), (3500, (5, 15)), (4221, (5, 16)), (5038, (5, 17)), (5957, (5, 18)), (6984, (5, 19)), (8125, (5, 20)), (432, (6, 6)), (559, (6, 7)), (728, (6, 8)), (945, (6, 9)), (1216, (6, 10)), (1547, (6, 11)), (1944, (6, 12)), (2413, (6, 13)), (2960, (6, 14)), (3591, (6, 15)), (4312, (6, 16)), (5129, (6, 17)), (6048, (6, 18)), (7075, (6, 19)), (8216, (6, 20)), (686, (7, 7)), (855, (7, 8)), (1072, (7, 9)), (1343, (7, 10)), (1674, (7, 11)), (2071, (7, 12)), (2540, (7, 13)), (3087, (7, 14)), (3718, (7, 15)), (4439, (7, 16)), (5256, (7, 17)), (6175, (7, 18)), (7202, (7, 19)), (8343, (7, 20)), (1024, (8, 8)), (1241, (8, 9)), (1512, (8, 10)), (1843, (8, 11)), (2240, (8, 12)), (2709, (8, 13)), (3256, (8, 14)), (3887, (8, 15)), (4608, (8, 16)), (5425, (8, 17)), (6344, (8, 18)), (7371, (8, 19)), (8512, (8, 20)), (1458, (9, 9)), (1729, (9, 10)), (2060, (9, 11)), (2457, (9, 12)), (2926, (9, 13)), (3473, (9, 14)), (4104, (9, 15)), (4825, (9, 16)), (5642, (9, 17)), (6561, (9, 18)), (7588, (9, 19)), (8729, (9, 20)), (2000, (10, 10)), (2331, (10, 11)), (2728, (10, 12)), (3197, (10, 13)), (3744, (10, 14)), (4375, (10, 15)), (5096, (10, 16)), (5913, (10, 17)), (6832, (10, 18)), (7859, (10, 19)), (9000, (10, 20)), (2662, (11, 11)), (3059, (11, 12)), (3528, (11, 13)), (4075, (11, 14)), (4706, (11, 15)), (5427, (11, 16)), (6244, (11, 17)), (7163, (11, 18)), (8190, (11, 19)), (9331, (11, 20)), (3456, (12, 12)), (3925, (12, 13)), (4472, (12, 14)), (5103, (12, 15)), (5824, (12, 16)), (6641, (12, 17)), (7560, (12, 18)), (8587, (12, 19)), (9728, (12, 20)), (4394, (13, 13)), (4941, (13, 14)), (5572, (13, 15)), (6293, (13, 16)), (7110, (13, 17)), (8029, (13, 18)), (9056, (13, 19)), (10197, (13, 20)), (5488, (14, 14)), (6119, (14, 15)), (6840, (14, 16)), (7657, (14, 17)), (8576, (14, 18)), (9603, (14, 19)), (10744, (14, 20)), (6750, (15, 15)), (7471, (15, 16)), (8288, (15, 17)), (9207, (15, 18)), (10234, (15, 19)), (11375, (15, 20)), (8192, (16, 16)), (9009, (16, 17)), (9928, (16, 18)), (10955, (16, 19)), (12096, (16, 20)), (9826, (17, 17)), (10745, (17, 18)), (11772, (17, 19)), (12913, (17, 20)), (11664, (18, 18)), (12691, (18, 19)), (13832, (18, 20)), (13718, (19, 19)), (14859, (19, 20)), (16000, (20, 20))]
  • Finally, we create a dictionary with the cube as the key and the (a, b) tuple pairs in a list as the values

In [61]:
#dict_of_cube_sums = {}
#for i in numbers_with_cube_sums:
#    if i[0] in dict_of_cube_sums:
#        dict_of_cube_sums[i[0]].append(i[1])
#    else:
#        dict_of_cube_sums[i[0]] = [i[1]]

dict_of_cube_sums = {}
[dict_of_cube_sums.setdefault(i[0], [])
for i in numbers_with_cube_sums]

[dict_of_cube_sums[i[0]].append(i[1])
for i in numbers_with_cube_sums]
print(dict_of_cube_sums)


{1024: [(8, 8)], 4097: [(1, 16)], 2: [(1, 1)], 1027: [(3, 10)], 513: [(1, 8)], 520: [(2, 8)], 9: [(1, 2)], 5642: [(9, 17)], 1547: [(6, 11)], 2060: [(9, 11)], 3087: [(7, 14)], 16: [(2, 2)], 8576: [(14, 18)], 2071: [(7, 12)], 8216: [(6, 20)], 8729: [(9, 20)], 539: [(3, 8)], 28: [(1, 3)], 6175: [(7, 18)], 432: [(6, 6)], 7202: [(7, 19)], 35: [(2, 3)], 1064: [(4, 10)], 3591: [(6, 15)], 559: [(6, 7)], 1072: [(7, 9)], 4104: [(2, 16), (9, 15)], 54: [(3, 3)], 5129: [(6, 17)], 4160: [(4, 16)], 65: [(1, 4)], 14859: [(19, 20)], 1008: [(2, 10)], 72: [(2, 4)], 9826: [(17, 17)], 6840: [(14, 16)], 91: [(3, 4)], 6750: [(15, 15)], 8288: [(15, 17)], 4706: [(11, 15)], 6244: [(11, 17)], 1125: [(5, 10)], 2662: [(11, 11)], 11375: [(15, 20)], 7471: [(15, 16)], 9331: [(11, 20)], 3197: [(10, 13)], 126: [(1, 5)], 128: [(4, 4)], 5824: [(12, 16)], 133: [(2, 5)], 3718: [(7, 15)], 5256: [(7, 17)], 1674: [(7, 11)], 2709: [(8, 13)], 2198: [(1, 13)], 5913: [(10, 17)], 152: [(3, 5)], 2205: [(2, 13)], 3744: [(10, 14)], 4123: [(3, 16)], 13832: [(18, 20)], 12913: [(17, 20)], 2728: [(10, 12)], 5427: [(11, 16)], 9928: [(16, 18)], 686: [(7, 7)], 6832: [(10, 18)], 1736: [(2, 12)], 7859: [(10, 19)], 3256: [(8, 14)], 2745: [(1, 14)], 8343: [(7, 20)], 189: [(4, 5)], 1216: [(6, 10)], 1729: [(1, 12), (9, 10)], 4439: [(7, 16)], 10955: [(16, 19)], 6344: [(8, 18)], 5833: [(1, 18)], 7371: [(8, 19)], 6860: [(1, 19)], 5840: [(2, 18)], 6867: [(2, 19)], 2261: [(4, 13)], 4312: [(6, 16)], 4825: [(9, 16)], 730: [(1, 9)], 1755: [(3, 12)], 2752: [(2, 14)], 224: [(2, 6)], 737: [(2, 9)], 5859: [(3, 18)], 6886: [(3, 19)], 2224: [(3, 13)], 637: [(5, 8)], 16000: [(20, 20)], 243: [(3, 6)], 756: [(3, 9)], 2808: [(4, 14)], 250: [(5, 5)], 11772: [(17, 19)], 1792: [(4, 12)], 3456: [(12, 12)], 8192: [(16, 16)], 5896: [(4, 18)], 6923: [(4, 19)], 12096: [(16, 20)], 728: [(6, 8)], 2322: [(5, 13)], 2771: [(3, 14)], 4375: [(10, 15)], 280: [(4, 6)], 793: [(4, 9)], 2331: [(10, 11)], 9009: [(16, 17)], 9000: [(10, 20)], 4394: [(13, 13)], 3887: [(8, 15)], 3376: [(1, 15)], 5425: [(8, 17)], 4914: [(1, 17)], 1843: [(8, 11)], 1332: [(1, 11)], 2869: [(5, 14)], 9728: [(12, 20)], 3383: [(2, 15)], 4921: [(2, 17)], 1339: [(2, 11)], 1853: [(5, 12)], 1343: [(7, 10)], 8512: [(8, 20)], 8001: [(1, 20)], 5957: [(5, 18)], 8008: [(2, 20)], 3402: [(3, 15)], 4940: [(3, 17)], 4941: [(13, 14)], 1358: [(3, 11)], 6984: [(5, 19)], 3925: [(12, 13)], 854: [(5, 9)], 855: [(7, 8)], 344: [(1, 7)], 8027: [(3, 20)], 8029: [(13, 18)], 351: [(2, 7)], 9056: [(13, 19)], 11664: [(18, 18)], 1512: [(8, 10)], 2413: [(6, 13)], 2926: [(9, 13)], 3439: [(4, 15)], 5488: [(14, 14)], 4977: [(4, 17)], 370: [(3, 7)], 1395: [(4, 11)], 7657: [(14, 17)], 4472: [(12, 14)], 217: [(1, 6)], 6293: [(13, 16)], 8064: [(4, 20)], 576: [(4, 8)], 9603: [(14, 19)], 7560: [(12, 18)], 8587: [(12, 19)], 2960: [(6, 14)], 3473: [(9, 14)], 12691: [(18, 19)], 13718: [(19, 19)], 407: [(4, 7)], 1944: [(6, 12)], 2457: [(9, 12)], 1241: [(8, 9)], 6048: [(6, 18)], 6561: [(9, 18)], 7075: [(6, 19)], 7588: [(9, 19)], 3500: [(5, 15)], 5038: [(5, 17)], 1456: [(5, 11)], 945: [(6, 9)], 1458: [(9, 9)], 8125: [(5, 20)], 4608: [(8, 16)], 5572: [(13, 15)], 7110: [(13, 17)], 3528: [(11, 13)], 2000: [(10, 10)], 468: [(5, 7)], 10197: [(13, 20)], 6119: [(14, 15)], 5096: [(10, 16)], 1001: [(1, 10)], 4075: [(11, 14)], 2540: [(7, 13)], 5103: [(12, 15)], 4221: [(5, 16)], 6641: [(12, 17)], 3059: [(11, 12)], 9207: [(15, 18)], 10744: [(14, 20)], 10745: [(17, 18)], 10234: [(15, 19)], 7163: [(11, 18)], 2240: [(8, 12)], 8190: [(11, 19)], 341: [(5, 6)]}

In [70]:
# Find the elements which have more than one element
# in their list
ramanujan_candidates = list(
    filter(lambda i : len(i[1]) > 1,
        dict_of_cube_sums.items()))
ramanujan_candidates


Out[70]:
[(4104, [(2, 16), (9, 15)]), (1729, [(1, 12), (9, 10)])]

Functions within functions

  • You can define a function within another

In [83]:
def add_n(n):
    def adder(a):
        return a + n
    return adder

add10 = add_n(10)
print(add10(23))
add3 = add_n(3)
print(add3(23))

def logger(func):
    def inner(*args, **kwargs):
        print("Arguments: %s %s" % (args, kwargs))
        return func(*args, **kwargs)
    return inner

def add_two_numbers(x, y):
    return x + y

print("Adding %d and %d gives %d" % (1, 3,
                                    add_two_numbers(1, 3)))


33
26
Adding 1 and 3 gives 4

In [85]:
logged_add_two_numbers = logger(add_two_numbers)
mysum = logged_add_two_numbers(1, 3)
print(mysum)


Arguments: (1, 3) {}
4