Published on

Functions in Python

Authors

Introduction

Python functions serve as the building blocks of code, promoting reusability and modularity. They encapsulate a set of instructions, allowing us to execute a block of code by calling the function. Let's delve into the basics of Python functions.

Basic Syntax of a Function

In Python, the def keyword is used to define a function. Parameters and return statements within the function define its behavior. This syntax forms the foundation for creating powerful and versatile functions.

main.py
def  <function_name>(para1, para2):
  statement
  return [expression]

If not returning any value then default return is None

main.py
# Example

def  test():
  print("hello world")

print(test())

Calling Stack

In Python, the call stack is a data structure that keeps track of the function calls during the execution of a program. It operates on a Last-In, First-Out (LIFO) basis, meaning that the last function called is the first one to finish. Each function call adds a new frame to the stack, and when a function completes, its frame is removed.

main.py
def function2():
print("Inside funciton2")

def function1():
  print("Inside function1")
  function2()
  print("Back in function1")

def main():
  print("Inside main")
  function1()

main()

Global and Local Variable

Global Variables

  • A global variable is one that is defined outside of any function or class.
  • It can be accessed from any part of the code, including inside functions.
  • To modify a global variable from within a function, you need to use the global keyword.
main.py
# Global variable
global_var = 10

def my_function():
    # Accessing global variable
    print("Inside the function:", global_var)

# Accessing global variable outside the function
print("Outside the function:", global_var)

# Modifying global variable inside the function
def modify_global():
    global global_var
    global_var = 20

modify_global()
print("After modification:", global_var)

Local Variables

  • A local variable is one that is defined within a function or block of code.
  • It is only accessible within that specific function or block.
  • Local variables do not interfere with variables of the same name in other scopes.
main.py
def my_function():
    # Local variable
    local_var = 5
    print("Inside the function:", local_var)

# Accessing local variable outside the function will result in an error
# print("Outside the function:", local_var)  # Uncommenting this line will raise an error

my_function()

Types of Argument:

Positional

  • These are the most common type of arguments.
  • The values are assigned based on their position.
main.py
def add_numbers(a, b):
    return a + b

result = add_numbers(2, 3)
print(result)  # Output: 5

Keyword

  • Arguments identified by the parameter name.
  • Order doesn't matter.
main.py
def greet(name, message):
    print(f"Hello, {name}! {message}")

greet(name="Alice", message="How are you?")

If you have introduced the keywrod argument than you cannot use argument without it

Variable-Length Positional Arguments (Arbitrary Arguments

  • Allows a function to accept any number of positional arguments.
  • The arguments are collected into a tuple.
main.py
def add_all(*numbers):
    return sum(numbers)

result = add_all(1, 2, 3, 4, 5)

  • → Arbitrary argument, it can accept any no. of arguments.

Variable-Length Keyword Arguments (Arbitrary Keyword Arguments)

  • Allows a function to accept any number of keyword arguments.
  • The arguments are collected into a dictionary.
main.py
def print_info(**info):
    for key, value in info.items():
        print(f"{key}: {value}")

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

Order to follow while combining argument types:

  • Functions can use a combination of different argument types.
main.py

def add(mn1, mn2, *vararg, koa, **keyvararg):
  print(mn1, mn2, vararg, koa, keyvararg)

Function decorator

In Python, a decorator is a special type of function that can be used to modify the behavior of another function. Decorators are often used to add functionality to existing functions or methods.

main.py
  def test():
    print("hello world")

  def logic(arg):
    arg()
    return arg

  test()
  res = logic(test)
  res()

Are the function which provides some fucntionality to function without affecting the function code.

main.py

def msg():
  print("Hello world!")

def makeBold(fun):
  def boldLogic():
    return "<b>" + fun() 9 "</b>"
  return bold_logic

msg = makeBold(msg)  # old way of decorators 
print(msg())

New way of decorators using @

main.py

@makeBold
def msg():
  print("Hello world!")

def makeBold(fun):
  def boldLogic():
    return "<b>" + fun() 9 "</b>"
  return bold_logic

print(msg())

Memorisation Techinque

Memoization is a technique used to optimize the performance of functions by caching their results. A decorator can be employed to implement memoization in Python.

main.py

@memorize
def fibonacci(n):
  if (n<=1):
    return n
  else:
    return fibonacci(n-1) + fibonacci(n-2)

def memorize(func):
  cache = {}
  def fiboLogic(n):
    if n not in cache:
      cache[n] = fiboLogic(n)
    return cache[n]
  
  return fiboLogic

Iter

If youre referring to the iter() function in Python, it's used to create an iterator object from an iterable. The iterator can be used to traverse the items of the iterable one at a time.

main.py

res = iter([i for i in range(2,2001,2)])

print(res)
print(next(res))

Generators

Generators in Python are a way to create iterators, providing a convenient way to produce a sequence of values without the need to create and store the entire sequence in memory. This is especially useful when dealing with large datasets or when you want to generate values on-the-fly. Generators are defined using a function with the yield keyword.

main.py
def countdown(n):
    while n > 0:
        yield n
        n -= 1

for i in countdown(5):
    print(i)

Key points about generators:

  • Lazy Evaluation: Values are generated on-the-fly and are not stored in memory all at once. This allows generators to be memory-efficient.
  • State Retention: The state of the generator function is retained between calls. The function resumes execution from where it left off when the next value is requested.
  • Infinite Sequences: Generators can represent infinite sequences since values are generated one at a time.

Lambda function

A lambda function in Python is a concise way to create small, anonymous functions. Lambda functions are defined using the lambda keyword, followed by a list of arguments, a colon, and an expression.

main.py
lambda arguments: expression
main.py
add = lambda x, y: x + y

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

Lambda functions are often used for short, simple operations where a full function definition would be more verbose. They are commonly used in functional programming constructs like map, filter, and sorted.

For example, using lambda with map to square each element in a list:

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

While lambda functions are powerful for concise expressions, keep in mind that they are limited compared to regular functions. They can only contain a single expression, and their use is typically reserved for situations where brevity is a priority.

Conclusion

In conclusion, Python functions form the backbone of code organization and reusability. From basic syntax to advanced topics like decorators and functional programming, mastering functions is essential for any Python developer. Dive into the world of functions and elevate your Python programming skills.

FAQs

  1. Can I use functions from Python libraries in my code?

Absolutely! Python offers a rich set of built-in functions and libraries that you can leverage in your code.

  1. How do I handle errors in my functions?

Use try-except blocks to gracefully handle errors and ensure your program doesn't crash unexpectedly.

  1. Are lambda functions necessary, or can I stick to regular functions?

Lambda functions are optional but provide a concise way to define small, one-time-use functions.

  1. What's the advantage of using generators in Python?

Generators offer memory-efficient iteration over large datasets, avoiding the need to load everything into memory at once.

  1. Should I always aim for recursion in my functions?

Recursion is powerful but not always necessary. Consider its pros and cons based on the specific problem at hand.