Let’s explain the concept of a function in a way that’s easy for kids to understand:

# Let's create a simple function to help us understand what a function is.

def greet(name):
    """ 
    This function says hello to someone.

    Input:
    - name: the name of the person you want to greet

    Output:
    - a friendly greeting
    """
    message = "Hello, " + name + "!"
    return message

# Now, let's use our function to greet different people.

# Greeting Mom
mom_greeting = greet("Mom")
print(mom_greeting)

# Greeting Dad
dad_greeting = greet("Dad")
print(dad_greeting)

# Greeting a friend
friend_greeting = greet("Friend")
print(friend_greeting)

Explanation:

  1. What’s a Function?
  • A function is a block of reusable code that performs a specific task or operation on data.
  • A function is like a little helper that does a specific task for us.
  • Imagine it’s a robot that follows our instructions.

2. Creating a Function:

  • We create a function named greet. Think of it as a robot that says hello to someone.

3. How the Function Works:

  • The function needs some information to work. In our case, it needs a name.
  • It takes the name, adds it to a greeting message, and gives us back the friendly message.

4. Using the Function:

  • Now, we use our friendly robot to greet different people.
  • First, we ask it to greet “Mom,” and it gives us a special message for Mom.
  • Then, we ask it to greet “Dad” and “Friend,” and it creates messages for them too.

5. Understanding Inputs and Outputs:

  • The input is like telling the robot what you want (in this case, the name to greet).
  • The output is like what the robot gives you back (in this case, the friendly greeting).

Remember, a function is like having a little friend who can do specific things for you. You tell it what you need, and it helps you out!

# Let's create a function to check if a number is odd or even.

def is_even(num):
  """
  This function returns if a given number is odd or even.

  Input:
  - num: any valid integer

  Output:
  - 'even' if the number is even
  - 'odd' if the number is odd
  - 'are you crazy' if the input is not an integer

  Created on: 16th Nov 2022
  """
  # Check if the input is an integer
  if type(num) == int:
    # Check if the number is even
    if num % 2 == 0:
      return 'even'
    else:
      return 'odd'
  else:
    return 'are you crazy?'  # Return a message if the input is not an integer

# Using the function in a loop
# Loop from 1 to 10
for i in range(1, 11):
  # Call the function for each number and store the result in variable 'x'
  x = is_even(i)
  # Print the result
  print(x)

# Print the docstring of the 'type' class
print(type.__doc__)

# Testing the function with a non-integer input
# This will print 'are you crazy' since 'hello' is not a valid integer
print(is_even('hello'))

Explanation:

  1. We create a function called is_even that takes a number as input and returns whether it’s odd or even.
  2. The function includes a docstring, which is a description of what the function does, what inputs it expects, and what it returns.
  3. Inside the function, it checks if the input is an integer. If it is, it checks if the number is even and returns ‘even’ or ‘odd’ accordingly. If the input is not an integer, it returns a message, ‘Are you crazy?’ .
  4. The function is then used in a loop from 1 to 10, and the results are printed.
  5. The docstring of the ‘type’ class is printed, showing how docstrings can provide information about classes or functions.
  6. Finally, the function is tested with a non-integer input (‘hello’), and it returns ‘are you crazy?’ as expected.

Parameters Vs Arguments

Types of Arguments

In programming, we often use words like “parameters” and “arguments.” Let’s break down these concepts using a simple example:

# Function definition with default parameters
def power(a=1, b=1):
    return a ** b

# Using the function with no specified arguments (uses default values)
power()

# Using positional arguments (order matters)
power(2, 3)

# Using keyword arguments (specifying which value goes with which parameter)
power(b=3, a=2)
  • Parameters: Think of them as empty slots or placeholders in a recipe. In our example, a and b are parameters.
  • Arguments: These are the actual values you put into those slots when you’re using the recipe. For example, in power(2, 3), 2 and 3 are the arguments.

*args and **kwargs

Now, let’s talk about some special techniques using *args and **kwargs:

# Using *args to accept any number of non-keyword arguments
def multiply(*args):
    product = 1

    for i in args:
        product = product * i

    print(args)
    return product

multiply(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12)

# Using **kwargs to accept any number of keyword arguments
def display(**kwargs):
    for (key, value) in kwargs.items():
        print(key, '->', value)

display(india='delhi', srilanka='colombo', nepal='kathmandu', pakistan='islamabad')
  • *args: It’s like a magic bag that can hold any number of items (arguments).
  • **kwargs: It’s like a magic bag that not only holds items but each item has a special tag (key-value pair).

Points to remember:

  • The order of the arguments matters: normal parameters -> *args -> **kwargs.
  • “args” and “kwargs” are just names; you can use any names you like.

How Functions are Executed in Memory?

When we write a function, the computer stores it in its memory like a set of instructions. It’s like telling the computer, “Hey, here’s what you need to do when I ask you to do this task.”

Without Return Statement

L = [1, 2, 3]
print(L.append(4))
print(L)
  • Here, L.append(4) adds 4 to the list, but L.append(4) itself doesn’t return anything useful. So, print(L.append(4)) will print None, which means nothing.

Variable Scope

def g(y):
    print(x)
    print(x + 1)

x = 5
g(x)
print(x)
  • When the function g is called, it looks for the value of x in its surroundings. It finds the x defined outside the function, so it prints its value.
def f(y):
    x = 1
    x += 1
    print(x)

x = 5
f(x)
print(x)
  • Here, the x inside the function is different from the one outside. The function creates its own little world, and changes inside it don’t affect the x outside.
def h(y):
    x += 1

x = 5
h(x)
print(x)
  • This will result in an error because the function tries to use x without defining it inside the function.

Function with Return Statement

def f(x):
    x = x + 1
    print('in f(x): x =', x)
    return x

x = 3
z = f(x)
print('in main program scope: z =', z)
print('in main program scope: x =', x)
  • This function f not only prints something but also gives back a value with return. When you call the function, you get something back, and you can use it later.

In simple terms, functions are like magical procedures that you define once and can use whenever you want them to perform their special tasks. They make your code organized and make it easier to reuse your code in different parts of your program.

Nested Functions

def f():
    def g():
        print('inside function g')
        f()

    g()
    print('inside function f')

f()
  • Nested Functions: Here, we have a function f that contains another function g inside it. When f is called, it first calls g and then prints a message.
def g(x):
    def h():
        x = 'abc'

    x = x + 1
    print('in g(x): x =', x)
    h()
    return x

x = 3
z = g(x)
  • Nested Functions with Parameters: The function g takes a parameter x and has another function h inside it. The inner function h doesn’t affect the x from the outer function.
def g(x):
    def h(x):
        x = x + 1
        print("in h(x): x =", x)

    x = x + 1
    print('in g(x): x =', x)
    h(x)
    return x

x = 3
z = g(x)
print('in main program scope: x =', x)
print('in main program scope: z =', z)
  • Nested Functions with Parameters and Scope: This example shows how the scope of variables works in nested functions. Changes made inside the inner function don’t affect the outer function’s variables.

Functions are 1st Class Citizens

# Checking type and id of a function
def square(num):
    return num ** 2

type(square)
id(square)

# Reassigning a function to a variable
x = square
id(x)
x(3)

# Assigning a function to a variable and using it
a = 2
b = a
b

# Deleting a function
del square

# Trying to use the deleted function (will result in an error)
square(3)

# Storing a function in a list and using it
L = [1, 2, 3, 4, square]
L[-1](3)

# Storing a function in a set
s = {square}
s

# Returning a function from another function
def f():
    def x(a, b):
        return a + b
    return x

val = f()(3, 4)
print(val)

# Function as an argument to another function
def func_a():
    print('inside func_a')

def func_b(z):
    print('inside func_b')
    return z()

print(func_b(func_a))
  • Functions are 1st Class Citizens: In Python, functions are treated as first-class citizens. This means they can be assigned to variables, passed as arguments to other functions, stored in data structures, and returned from other functions.

Benefits of Using a Function

  • Code Modularity: Functions help break down a complex problem into smaller, more manageable parts.
  • Code Readability: By giving a meaningful name to a function, it becomes easier to understand what a particular piece of code is doing.
  • Code Reusability: Once a function is defined, it can be reused multiple times, reducing the need to rewrite the same code.

Lambda Function

A lambda function is like a tiny, unnamed superhero function that can do a quick task for us. It’s short, sweet, and only has one mission at a time.

# Lambda function to square a number
lambda x: x**2

# Lambda function to add two numbers
a = lambda x, y: x + y
a(5, 2)
  • No Name: Lambda functions are anonymous; they don’t have a proper name like regular functions.
  • No Return Statement: Lambda functions don’t explicitly use the return keyword. Instead, they implicitly return the result of the expression.
  • One-Liner: They are usually written in just one line, keeping things concise.
  • Not Reusable: Lambda functions are great for short tasks, but they aren’t meant for complex operations. They are often used on the fly for specific purposes.

Difference between Lambda and Normal Function

  • Lambda Has No Name: Normal functions have a defined name, making them easily recognizable in the code.
  • No Explicit Return: Lambda functions don’t need the return keyword; they automatically return the result of the expression.
  • One-Liner: Lambda functions are typically written in one line, making them suitable for quick tasks.
  • Not Reusable: Lambda functions are concise but not designed for extensive reuse.

Then why use lambda functions? They are often used with Higher Order Functions (HOF).

# Checking if 'a' is in a string
a = lambda s: 'a' in s
a('hello')

# Odd or even labeling
a = lambda x: 'even' if x % 2 == 0 else 'odd'
a(6)

Higher Order Functions (HOF)

Higher Order Functions are like bosses that can handle functions as if they were data.

# Square and cube functions
def square(x):
    return x**2

def cube(x):
    return x**3

# Higher Order Function to transform a list
def transform(f, L):
    output = []
    for i in L:
        output.append(f(i))

    print(output)

L = [1, 2, 3, 4, 5]
transform(lambda x: x**3, L)

Map

Map is like a magical spell that transforms every member of a list.

# Squaring items in a list
list(map(lambda x: x**2, [1, 2, 3, 4, 5]))

# Labeling odd or even in a list
L = [1, 2, 3, 4, 5]
list(map(lambda x: 'even' if x % 2 == 0 else 'odd', L))

# Fetching names from a list of dictionaries
users = [
    {'name': 'Nabin', 'age': 45, 'gender': 'male'},
    {'name': 'Naksh', 'age': 33, 'gender': 'male'},
    {'name': 'Nabina', 'age': 50, 'gender': 'female'}
]

list(map(lambda user: user['gender'], users))

Filter

Filter is like a superhero who only lets certain members pass through.

# Filtering numbers greater than 5
L = [3, 4, 5, 6, 7]
list(filter(lambda x: x > 5, L))

# Filtering fruits starting with 'a'
fruits = ['apple', 'guava', 'cherry']
list(filter(lambda x: x.startswith('a'), fruits))

Reduce

Reduce is like a wizard that combines all members into one.

# Sum of all items
import functools
functools.reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])

# Finding the minimum
functools.reduce(lambda x, y: x if x > y else y, [23, 11, 45, 10, 1])

Lambda functions and Higher Order Functions add a bit of magic to programming, making our code more elegant and powerful.

2 Replies to “Functions in Python”

Leave a Reply

Your email address will not be published. Required fields are marked *