Functions, functional programming

Python

Functions

Author

Chi Zhang

Published

December 5, 2024

Functional programming

A function can be assigned to another. The same functionality exists in R.

def add_one(x):
  return x+1
add_one(10)

# now assign this function to another, Add_One
Add_One = add_one
Add_One(10)
11

A function can be the argument of another function

def add_two(x):
  return x+2

def add_value_string(x, function):
  return 'result is '+ str(function(x))

add_value_string(10, add_one)
add_value_string(10, add_two)
'result is 12'

Lambda expressions

These are functions that are without a name, and has a lambda keyword. Typically they do not have a name, but you can also assign a name so that it functions like a normal function.

lambda x:x+5
add_five = lambda x:x+5
add_five(3)
8
#def greet(name):
#  return 'Welcome, ' + name
greet = lambda name: 'Welcome, ' + name

print(greet('Puff'))
print(greet('Ponpon'))
Welcome, Puff
Welcome, Ponpon

Multiple arguments

lambda a, b: a*b + 5

Execute

res = (lambda x, y: x+y)(2, 3)
res
5

Lambda expressions are useful as a function factory,

def mult(n):
  return lambda a : a * n

doubler = mult(2)
tripler = mult(3)

print(doubler(5))
print(tripler(5))
10
15

Maps and filters

Syntax: map(<function>, <iterable>)

The iterable does not have to be a list: it can also be a dictionary

On lists

#List of names in various cases
names = ["alice", "bob", "CHARLIE", "dEborah"]

# Function to capitalize each name
def capitalize(name):
  return name.capitalize()

# Using map() to apply the capitalization to each name
capitalized = map(capitalize, names)

# Converting map object to a list
capitalized = list(capitalized)

print(capitalized)
['Alice', 'Bob', 'Charlie', 'Deborah']

Combined with lambdas

numbers = [1, 2, 3]
doubled = list(map(lambda x: x*2, numbers)) # first part is function
doubled
[2, 4, 6]

Filters on a list

products = ["Table", "Sofa", "Cushion", "Bookshelf", "Vase"]

# Filters products with name length equal to 4
filtered_prod = list(filter(lambda name: len(name) == 4, products))

print(filtered_prod)
['Sofa', 'Vase']

On dictionary

products = {'Table': 110, 'Sofa': 120, 'Chair': 45, 'Lamp': 70}

#filtering products with prices less than 90
filtered_products = dict(filter(lambda item: item[1] < 90, products.items()))

print(filtered_products)
{'Chair': 45, 'Lamp': 70}

args and kwargs

The difference between *args and **kwargs is that the former receives a tuple, the later receives a dictionary.

def total(numbers):
  result = 0
  # iterate over the list
  for i in numbers:
    result += i
  return result

nums = [1,2,3,4]
print(total(nums))
10

*args

*args allows any number of arguments without creating a list before calling. It receives arguments as a tuple (an iterable)

def total(*args): # can also be *number
  result = 0
  for arg in args: # no *, since it's unpacked
    result += arg
  return result

print(total(1,2,3)) # no need to create a list, here we have 3 arguments instead of a list
print(total(201,23))
6
224

Recall unpacking in tuples, use * operator. It unpacks a tuple as a list

three_args = (76, 81, 96)
arg1, arg2, arg3 = three_args
print(arg1)
arg1, *args = three_args # this is where args is
print(args)
76
[81, 96]

The args does not have to be args, it can take any name as long as the operator is there.

def display(*words):
  for item in words: # here no *, since it is unpacked!
    print(item)

display('word1')
display('word1', 'word3', 'word4')
display('word1', 3, 24.1) # doesn't have to be a string
word1
word1
word3
word4
word1
3
24.1

When combining with regular arguments, the regular ones must come before *args

def<func> (<argument>, <*args>)

def show_items(category, *items):
  print("Category: " + category)
  for item in items:
    print(item)

show_items("Electronics", "Laptop", "Smartphone", "Tablet") # the first is the category
Category: Electronics
Laptop
Smartphone
Tablet

**kwargs: keyword arguments

This one receives argument in the form of a dictionary that have key:value pairs. The ** operator unpacks dictionaries into arguments.

#**kwargs is a dictionary
def display_info(**kwargs):
  #kwargs.items() returns the key:value pairs
  for key, value in kwargs.items():
    print(key, ":", value)

display_info(name="Alice", age=30, city="New York")
name : Alice
age : 30
city : New York
person = {'name':"Alice", 'age':30, 'city':"New York"}
person.items() # all pairs
dict_items([('name', 'Alice'), ('age', 30), ('city', 'New York')])

Put together

def<func> (<argument>, <*args>, <**kwargs>)