Python Diaries ….. Day 10

Functions

Nishitha Kalathil
11 min readSep 19, 2023

--

Welcome to Day 10 of Python Diaries! Today, we’re delving into the concept of functions. Functions are blocks of reusable code that perform a specific task. They allow you to organize code into modular, reusable pieces, making your programs more readable and maintainable.

Type of Functions

Defining a function

Variable Scope

Recursive Functions

Lambda Functions

Libraries, Modules and Packages

Standard Libraries

User-Defined Modules

ways for importing libraries and modules

What is a Function?

A function in Python is a named block of code that performs a specific task or a set of tasks. Functions can take input (called arguments or parameters) and return output (a value or a set of values). They allow you to break down complex tasks into smaller, manageable pieces.

Types of functions

In Python, there are several types of functions, each with its own purpose and characteristics. Here are the main types of functions:

> Built-in Functions:

These are functions that are built into Python and are always available for use. Examples include print(), len(), range(), and input().

> User-Defined Functions:

> These are functions that are defined by the user. You can create your own functions to perform specific tasks and reuse them throughout your code.

> Anonymous Functions (Lambda Functions):

These are small, one-line functions defined without a name. They are typically used in situations where you need a quick, throwaway function.

Defining a User-Defined Functions

Here’s the basic syntax for defining a function:

def function_name(parameters):
# Code block for the function
# ...
return result # Optional
  • def: This keyword is used to define a function.
  • function_name: This is the name of the function. It should be descriptive of the task the function performs.
  • parameters: These are the input values that the function can accept. They are optional.
  • return: This keyword is used to specify the value(s) that the function should return. It's optional; a function can return nothing.

Function Definition

Here is an example for a simple function:

def greet():
print("Hello!")

Here, we’ve defined a simple function called greet .

Calling a Function

Once a function is defined, you can call it by using its name followed by parentheses containing the arguments.

greet()   # Output: Hello

which, when called, prints "Hello!".

Function Parameters

Functions can take parameters, which are values passed to the function when it is called.

def greet(name):
print(f"Hello, {name}!")

# Call the function with an argument
greet("Alice") # Output: Hello, Alice!

Return Statement

The return statement in Python is used to specify what value a function should return. It ends the execution of the function and sends the specified value back to the caller.

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

result = add(3, 5)
print(result) # Output: 8

A function can have multiple return statements, but only one of them will be executed. The function will exit as soon as it hits a return statement.

def is_positive(num):
if num > 0:
return True
else:
return False

Functions can return multiple values by packing them into a data structure like a tuple. This allows for more complex data to be returned from a function.

def calculate_stats(numbers):
total = sum(numbers)
average = total / len(numbers)
return total, average

result = calculate_stats([1, 2, 3, 4, 5]) # result will be (15, 3.0)

Once a function returns a value, you can use it in various ways:

  • Assign it to a variable for later use.
  • Use it directly in expressions.
  • Pass it as an argument to another function.
def square(x):
return x ** 2

result = square(4) # result will be 16

Default Arguments

You can provide default values for function parameters. If a value is not passed for that parameter when calling the function, the default value will be used.

def greet(name="User"):
return f"Hello, {name}!"

greeting = greet()
print(greeting) # Output: Hello, User!

Keyword Arguments

You can also pass arguments by keyword.

def greet(first_name, last_name):
print(f"Hello, {first_name} {last_name}!")

greet(last_name="Doe", first_name="John") # Output: Hello, John Doe!

Variable-Length Argument Lists

Variable-Length Argument Lists, often referred to as *args and **kwargs in Python, allow functions to accept an arbitrary number of arguments. They are very useful when you need to create flexible functions that can handle different numbers of arguments. They are commonly used in libraries and frameworks to provide a flexible API for users.

*args (Arbitrary Positional Arguments):

The *args syntax allows a function to receive an arbitrary number of positional arguments. These arguments are passed as a tuple and can be accessed within the function using a loop or indexing.

def sum_numbers(*args):
total = 0
for num in args:
total += num
return total

result = sum_numbers(1, 2, 3, 4)
print(result) # Output: 10

In this example, *args collects all the arguments passed to sum_numbers and treats them as a tuple ((1, 2, 3, 4) in this case).

*kwargs (Arbitrary Keyword Arguments):

The **kwargs syntax allows a function to receive an arbitrary number of keyword arguments. These arguments are passed as a dictionary, where the keys are the argument names and the values are the corresponding values.

def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")

print_info(name="Alice", age=30, city="New York")

In this example, **kwargs collects the keyword arguments and treats them as a dictionary ({'name': 'Alice', 'age': 30, 'city': 'New York'} in this case).

Combining *args and **kwargs:

You can use both *args and **kwargs in the same function definition.

def example_function(*args, **kwargs):
print("Positional arguments (*args):", args)
print("Keyword arguments (**kwargs):", kwargs)

example_function(1, 2, 3, name="Alice", age=30)

In this example, *args collects the positional arguments (1, 2, 3) and **kwargs collects the keyword arguments {'name': 'Alice', 'age': 30}.

Using *args and **kwargs with Normal Arguments:

You can use *args and **kwargs along with normal arguments, but keep in mind that they must come after the normal arguments.

def example_function(normal_arg, *args, **kwargs):
print("Normal Argument:", normal_arg)
print("Positional arguments (*args):", args)
print("Keyword arguments (**kwargs):", kwargs)

example_function("Hello", 1, 2, 3, name="Alice", age=30)

In this example, normal_arg will receive the value "Hello", *args will receive (1, 2, 3), and **kwargs will receive {'name': 'Alice', 'age': 30}.

Variable Scope

The scope of variables in functions refers to the area of a program where a variable can be accessed or referenced. In Python, there are two main types of variable scope: global and local.

1. Global Scope:

  • Variables defined outside of any function have global scope.
  • They can be accessed from anywhere in the program, including inside functions.
  • Example:
x = 10  # Global variable

def func():
print(x) # Accessible inside the function

func() # Output: 10
print(x) # Output: 10

2. Local Scope:

  • Variables defined inside a function have local scope.
  • They can only be accessed within the function in which they are defined.
  • Example:
def func():
y = 20 # Local variable
print(y)

func() # Output: 20

# Attempting to access y outside the function will result in an error
# print(y) # This will raise a NameError

3. Shadowing:

  • If a variable with the same name exists both globally and locally, the local variable shadows the global one within the function’s scope.
  • Example:
x = 10  # Global variable

def func():
x = 20 # Local variable with the same name as the global one
print(x) # This will print the local x

func() # Output: 20
print(x) # Output: 10 (Global x remains unchanged)

4. Accessing Global Variables Locally:

  • If you want to modify a global variable from within a function, you need to use the global keyword to indicate that you're referring to the global variable, rather than creating a new local one.
  • Example:
x = 10  # Global variable

def func():
global x # Indicate that x is the global variable
x = 20 # Modify the global x
print(x)

func() # Output: 20
print(x) # Output: 20 (Global x has been modified)

5. Nonlocal Keyword (Nested Functions):

  • When you have nested functions (a function inside another function), you may encounter the need to modify a variable in an outer scope.
  • The nonlocal keyword allows you to do this.
  • Example:
def outer():
x = 10 # Local variable in outer function

def inner():
nonlocal x # Indicate that x is in the outer scope
x = 20 # Modify the variable in outer scope
print(x)

inner() # Output: 20
print(x) # Output: 20

outer()

Recursive Functions

A recursive function is a function that calls itself. It’s a powerful technique for solving problems that can be broken down into smaller, similar subproblems.

def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

result = factorial(5)
print(result) # Output: 120

Lambda Functions

Lambda functions, also known as anonymous functions or lambda expressions, are a concise way to define small, simple functions in Python. They are often used for operations that require a function, but where defining a full-fledged named function is overkill. Lambda functions are particularly handy for short, one-off operations. However, they’re not suitable for complex or multi-step operations where you might want to use a named function for clarity and reusability.

Here’s the basic syntax of a lambda function:

lambda arguments: expression
  • lambda: This is the keyword that indicates you're creating a lambda function.
  • arguments: These are the input parameters to the function. You can have any number of arguments, separated by commas.
  • expression: This is the computation the function performs. The result of this expression is implicitly returned.

Here’s an example:

double = lambda x: x * 2

In this example, we’ve created a lambda function that takes one argument x and returns x * 2.

Lambda functions are often used in scenarios where you need to pass a simple function as an argument to another function, like in map, filter, or sorted.

Example: Using Lambda with map()

The map() function applies a given function to all items in a list and returns an iterator.

numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x**2, numbers)
print(list(squared)) # Output: [1, 4, 9, 16, 25]

In this example, the lambda function lambda x: x**2 is applied to each element in the numbers list.

Example: Using Lambda with filter()

The filter() function creates an iterator from elements of an iterable for which a function returns true.

numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers)) # Output: [2, 4]

In this example, the lambda function lambda x: x % 2 == 0 filters out the even numbers from the numbers list.

Example: Using Lambda with sorted()

The sorted() function sorts an iterable and returns a new sorted list.

names = ['Alice', 'Bob', 'Charlie', 'David']
sorted_names = sorted(names, key=lambda x: len(x))
print(sorted_names) # Output: ['Bob', 'Alice', 'David', 'Charlie']

In this example, we sort the list of names based on their lengths using the lambda function lambda x: len(x).

Libraries, Modules and Packages in Python

In Python, libraries and modules are key concepts that allow you to organize and reuse code effectively. They help manage complexity, promote code reusability, and facilitate collaboration among developers.

  • Packages are not modules or libraries themselves. Instead, they are containers for modules. A package can contain multiple modules, which in turn can contain functions, classes, and variables.
  • Packages can be seen as a way to organize and structure modules. They allow you to group related functionality together in a hierarchical manner.

Built-in Functions and Libraries

Built-in functions are pre-defined functions in Python that are readily available for use without the need to define them. They serve as powerful tools to perform various operations in your code. Most commonly used built-in functions are print(), len(), input(), range(), int(), str(), and many more.

Standard Libraries:

Python includes a comprehensive set of standard libraries that offer a wide array of functionalities. These libraries provide pre-written, tested code that you can use to accomplish various tasks without having to start from scratch. Here are a few examples:

Math Library (math): Provides a range of mathematical operations and functions.

import math

result = math.sqrt(25) # Calculates the square root: 5.0

Random Library (random): Allows for generating random numbers and performing random selections.

import random

rand_num = random.randint(1, 10) # Generates a random integer between 1 and 10

OS Library (os): Provides a way to interact with the operating system, allowing you to perform tasks like navigating directories, working with files, and executing system commands.

import os

current_dir = os.getcwd() # Gets the current working directory

Datetime Library (datetime): Offers classes for manipulating dates and times.

from datetime import datetime

now = datetime.now() # Gets the current date and time

JSON Library (json): Facilitates the encoding and decoding of JSON data.

import json

data = {'name': 'Alice', 'age': 30}
json_data = json.dumps(data) # Converts a Python dictionary to a JSON string

User-Defined Modules:

User-defined modules allow you to organize your code into separate files, enhancing manageability and reusability. By creating your own modules, you can encapsulate related functions, classes, and variables for use across different parts of your program or in entirely different projects. Here’s how you can work with user-defined modules:

Defining a Module:

  • Create a Python file (.py) containing your functions, classes, or variables.
  • Example (module named my_module.py):
def greet(name):
return f"Hello, {name}!"

Importing a Module:

  • Use the import keyword to bring in the module into your program.
import my_module

result = my_module.greet("Bob") # Calls the greet function from my_module

Using Imported Functions or Variables:

  • Once imported, you can use the functions, classes, or variables defined in the module.
print(result)  # Output: "Hello, Bob!"

User-defined modules are particularly valuable in larger projects where you want to structure your code for better maintainability. They promote code organization, reuse, and separation of concerns.

ways for importing libraries and modules

In Python, there are several ways to import libraries and modules, depending on how you want to access the contents. Here are the different methods:

1. Importing the Entire Library or Module:

You can import the entire library or module, and then access its contents using dot notation.

import math

result = math.sqrt(25) # Calculates the square root: 5.0

2. Importing Specific Functions/Variables/Classes:

You can selectively import specific functions, variables, or classes from a library or module.

from math import sqrt

result = sqrt(25) # Calculates the square root: 5.0

3. Importing with an Alias:

You can import a library or module with an alias to give it a shorter or more convenient name.

import math as m

result = m.sqrt(25) # Calculates the square root: 5.0

4. Importing Everything from a Module (Not Recommended):

You can use a wildcard * to import all contents from a module. However, this is generally discouraged as it can lead to namespace pollution.

from math import *

result = sqrt(25) # Calculates the square root: 5.0

5. Importing Submodules or Subpackages:

For larger libraries, you might want to import specific submodules or subpackages.

from urllib.request import urlopen

response = urlopen('https://www.example.com')

I know it’s a looong note on functions, but I am sure it’s worth it for you. Functions are a fundamental building block of any programming language. They allow you to break down complex tasks, improve code reusability, and enhance the readability of your programs. Mastering the art of writing effective functions is a key skill in Python programming.

You can access the other topics in this tutorial series right here:

In Day 11, we’ll explore file handling, which are essential for organizing and sharing code in larger projects. Keep up the great work! Happy coding!

--

--